From 1e797aa329b18c70277bd352a606da1aeb874589 Mon Sep 17 00:00:00 2001 From: Alex North <445306+anorth@users.noreply.github.com> Date: Mon, 4 Sep 2023 11:13:46 +1200 Subject: [PATCH] Basic unit tests for ProveReplicaUpdate3 --- Cargo.lock | 1 + actors/miner/Cargo.toml | 1 + actors/miner/src/lib.rs | 29 +- .../tests/miner_actor_test_precommit_batch.rs | 5 +- actors/miner/tests/prove_replica_test.rs | 161 +++++++ actors/miner/tests/util.rs | 452 +++++++++++------- runtime/src/test_utils.rs | 41 +- 7 files changed, 475 insertions(+), 215 deletions(-) create mode 100644 actors/miner/tests/prove_replica_test.rs diff --git a/Cargo.lock b/Cargo.lock index e855b11ef1..a82fa296cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1585,6 +1585,7 @@ dependencies = [ "fil_actor_market", "fil_actor_power", "fil_actor_reward", + "fil_actor_verifreg", "fil_actors_runtime", "frc42_dispatch", "fvm_ipld_amt", diff --git a/actors/miner/Cargo.toml b/actors/miner/Cargo.toml index d0cc03c114..005d942068 100644 --- a/actors/miner/Cargo.toml +++ b/actors/miner/Cargo.toml @@ -39,6 +39,7 @@ fil_actor_account = { workspace = true } fil_actor_reward = { workspace = true } fil_actor_power = { workspace = true } fil_actor_market = { workspace = true } +fil_actor_verifreg = { workspace = true } rand = { workspace = true } test-case = { workspace = true } diff --git a/actors/miner/src/lib.rs b/actors/miner/src/lib.rs index 7cc0f55b91..261c58987f 100644 --- a/actors/miner/src/lib.rs +++ b/actors/miner/src/lib.rs @@ -1045,7 +1045,7 @@ impl Actor { // Validate parameters. rt.validate_immediate_caller_is( - info.control_addresses.iter().chain(&[info.owner, info.worker]), + info.control_addresses.iter().chain(&[info.worker, info.owner]), )?; if params.sector_proofs.is_empty() == params.aggregate_proof.is_empty() { return Err(actor_error!( @@ -1154,7 +1154,7 @@ impl Actor { } let proven_batch = proven_batch_gen.gen(); - // Activate data and compute CommD. + // Activate data. let data_activation_inputs: Vec = proven_manifests .iter() .map(|(update, info)| SectorPiecesActivationInput { @@ -1855,7 +1855,7 @@ impl Actor { sector_expiry: precommit.info.expiration, sector_number: precommit.info.sector_number, sector_type: precommit.info.seal_proof, - expected_commd: Some(precommit.info.unsealed_cid.clone()), + expected_commd: Some(precommit.info.unsealed_cid.clone()), // Check CommD } }) .collect(); @@ -5365,8 +5365,10 @@ struct ReplicaUpdateActivatedData { // Activates data pieces by claiming allocations with the verified registry. // Pieces are grouped by sector and succeed or fail in sector groups. -// Activation inputs specify an expected CommD for the sector, -// against which the CommD computed from the pieces is checked. +// If an activation input specifies an expected CommD for the sector, a CommD +// is calculated from the pieces and must match. +// This method never returns CommDs in the output type; either the caller provided +// them and they are correct, or the caller did not provide anything that needs checking. fn activate_sectors_pieces( rt: &impl Runtime, activation_inputs: Vec, @@ -5374,16 +5376,15 @@ fn activate_sectors_pieces( ) -> Result<(BatchReturn, Vec), ActorError> { // Get a flattened list of verified claims for all activated sectors let mut verified_claims = Vec::new(); - let mut computed_commds = Vec::new(); let mut unverified_spaces = Vec::new(); for activation_info in activation_inputs { - let computed_commd = unsealed_cid_from_pieces( - rt, - &activation_info.piece_manifests, - activation_info.sector_type, - )?; // Check a declared CommD matches that computed from the data. if let Some(declared_commd) = activation_info.expected_commd { + let computed_commd = unsealed_cid_from_pieces( + rt, + &activation_info.piece_manifests, + activation_info.sector_type, + )?; if !declared_commd.eq(&computed_commd) { return Err(actor_error!( illegal_argument, @@ -5394,7 +5395,6 @@ fn activate_sectors_pieces( )); } } - computed_commds.push(computed_commd); let mut sector_claims = vec![]; let mut unverified_space = BigInt::zero(); @@ -5429,12 +5429,11 @@ fn activate_sectors_pieces( let activation_outputs = claim_res .sector_claims .iter() - .zip(claim_res.sector_results.successes(&computed_commds)) .zip(claim_res.sector_results.successes(&unverified_spaces)) - .map(|((sector_claim, computed_commd), unverified_space)| DataActivationOutput { + .map(|(sector_claim, unverified_space)| DataActivationOutput { unverified_space: unverified_space.clone(), verified_space: sector_claim.claimed_space.clone(), - unsealed_cid: computed_commd.0, + unsealed_cid: None, }) .collect(); diff --git a/actors/miner/tests/miner_actor_test_precommit_batch.rs b/actors/miner/tests/miner_actor_test_precommit_batch.rs index 59b4150638..0413fb60ca 100644 --- a/actors/miner/tests/miner_actor_test_precommit_batch.rs +++ b/actors/miner/tests/miner_actor_test_precommit_batch.rs @@ -148,7 +148,8 @@ mod miner_actor_precommit_batch { SectorDeals, VerifyDealsForActivationParams, VerifyDealsForActivationReturn, }; use fil_actor_miner::{ - new_deadline_info_from_offset_and_epoch, Actor, Method, PreCommitSectorBatchParams2, + new_deadline_info_from_offset_and_epoch, Actor, CompactCommD, Method, + PreCommitSectorBatchParams2, }; use fil_actors_runtime::{STORAGE_MARKET_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR}; use fvm_ipld_encoding::ipld_block::IpldBlock; @@ -332,7 +333,7 @@ mod miner_actor_precommit_batch { precommit_epoch - 1, sector_expiration, vec![1], - Some(make_piece_cid(&[1])), + CompactCommD::new(Some(make_piece_cid(&[1]))), ); let sectors = vec![sector]; { diff --git a/actors/miner/tests/prove_replica_test.rs b/actors/miner/tests/prove_replica_test.rs new file mode 100644 index 0000000000..1420ced779 --- /dev/null +++ b/actors/miner/tests/prove_replica_test.rs @@ -0,0 +1,161 @@ +use fil_actor_market::NO_ALLOCATION_ID; +use fvm_ipld_blockstore::Blockstore; +use fvm_shared::deal::DealID; +use fvm_shared::piece::PaddedPieceSize; +use fvm_shared::sector::SectorNumber; +use fvm_shared::{bigint::Zero, clock::ChainEpoch, econ::TokenAmount, ActorID}; + +use fil_actor_miner::ext::verifreg::AllocationID; +use fil_actor_miner::{ + CompactCommD, PieceActivationManifest, SectorOnChainInfo, SectorPreCommitInfo, + SectorPreCommitOnChainInfo, SectorUpdateManifest, State, VerifiedAllocationKey, +}; +use fil_actor_miner::{DataActivationNotification, ProveReplicaUpdates3Return}; +use fil_actors_runtime::cbor::serialize; +use fil_actors_runtime::test_utils::{make_piece_cid, make_sealed_cid, MockRuntime}; +use fil_actors_runtime::{runtime::Runtime, BatchReturn, EPOCHS_IN_DAY, STORAGE_MARKET_ACTOR_ADDR}; +use util::*; + +mod util; + +const DEFAULT_SECTOR_EXPIRATION_DAYS: ChainEpoch = 220; + +#[test] +fn prove_single_sectorxxx() { + 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); + + // Update them in batch, each with a single piece. + let st: State = h.get_state(&rt); + let store = rt.store(); + 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[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 + ]; + + let result = h.prove_replica_updates3_batch(&rt, §or_updates, true, true).unwrap(); + assert_eq!( + ProveReplicaUpdates3Return { activation_results: BatchReturn::ok(sectors.len() as u32) }, + result + ); +} + +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. + // Note: migrate this to ProveCommitSectors2 (batch) when the harness supports it. + rt.set_epoch(precommit_epoch + rt.policy.pre_commit_challenge_delay + 1); + let sectors: Vec = precommits + .iter() + .map(|pc| { + h.prove_commit_sector_and_confirm( + rt, + pc, + h.make_prove_commit_params(pc.info.sector_number), + ProveCommitConfig::default(), + ) + .unwrap() + }) + .collect(); + + // Window PoST to activate the sectors, a pre-requisite for upgrading. + h.advance_and_submit_posts(rt, §ors); + sectors +} + +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() +} + +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 make_piece_manifest( + seq: usize, + size: u64, + alloc_client: ActorID, + alloc_id: AllocationID, + deal: DealID, +) -> PieceActivationManifest { + PieceActivationManifest { + cid: make_piece_cid(format!("piece-{}", seq).as_bytes()), + size: PaddedPieceSize(size), + verified_allocation_key: if alloc_id != NO_ALLOCATION_ID { + Some(VerifiedAllocationKey { client: alloc_client, id: alloc_id }) + } else { + None + }, + notify: if deal != 0 { + vec![DataActivationNotification { + address: STORAGE_MARKET_ACTOR_ADDR, + payload: serialize(&deal, "deal id").unwrap(), + }] + } else { + vec![] + }, + } +} diff --git a/actors/miner/tests/util.rs b/actors/miner/tests/util.rs index a7ad3239ff..3ba4d0e860 100644 --- a/actors/miner/tests/util.rs +++ b/actors/miner/tests/util.rs @@ -26,15 +26,16 @@ use fvm_shared::crypto::hash::SupportedHashes; use fvm_shared::deal::DealID; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; -use fvm_shared::piece::PaddedPieceSize; +use fvm_shared::piece::{PaddedPieceSize, PieceInfo}; use fvm_shared::randomness::Randomness; use fvm_shared::randomness::RANDOMNESS_LENGTH; use fvm_shared::sector::{ - AggregateSealVerifyInfo, PoStProof, RegisteredPoStProof, RegisteredSealProof, SealVerifyInfo, - SectorID, SectorInfo, SectorNumber, SectorSize, StoragePower, WindowPoStVerifyInfo, + AggregateSealVerifyInfo, PoStProof, RegisteredAggregateProof, RegisteredPoStProof, + RegisteredSealProof, ReplicaUpdateInfo, SealVerifyInfo, SectorID, SectorInfo, SectorNumber, + SectorSize, StoragePower, WindowPoStVerifyInfo, }; use fvm_shared::smooth::FilterEstimate; -use fvm_shared::{MethodNum, HAMT_BIT_WIDTH, METHOD_SEND}; +use fvm_shared::{HAMT_BIT_WIDTH, METHOD_SEND}; use itertools::Itertools; use lazy_static::lazy_static; use multihash::derive::Multihash; @@ -47,8 +48,11 @@ use fil_actor_market::{ VerifyDealsForActivationParams, VerifyDealsForActivationReturn, }; use fil_actor_miner::ext::market::ON_MINER_SECTORS_TERMINATE_METHOD; -use fil_actor_miner::ext::power::{UPDATE_CLAIMED_POWER_METHOD, UPDATE_PLEDGE_TOTAL_METHOD}; -use fil_actor_miner::ext::verifreg::CLAIM_ALLOCATIONS_METHOD; +use fil_actor_miner::ext::power::UPDATE_CLAIMED_POWER_METHOD; +use fil_actor_miner::ext::verifreg::{ + AllocationClaim, ClaimAllocationsParams, ClaimAllocationsReturn, SectorAllocationClaims, + SectorClaimSummary, CLAIM_ALLOCATIONS_METHOD, +}; use fil_actor_miner::ext::verifreg::{ Claim as FILPlusClaim, ClaimID, GetClaimsParams, GetClaimsReturn, }; @@ -68,19 +72,22 @@ use fil_actor_miner::{ ExpirationQueue, ExpirationSet, ExtendSectorExpiration2Params, ExtendSectorExpirationParams, FaultDeclaration, GetAvailableBalanceReturn, GetBeneficiaryReturn, GetControlAddressesReturn, GetMultiaddrsReturn, GetPeerIDReturn, Method, MinerConstructorParams as ConstructorParams, - MinerInfo, Partition, PendingBeneficiaryChange, PoStPartition, PowerPair, - PreCommitSectorBatchParams, PreCommitSectorBatchParams2, PreCommitSectorParams, - ProveCommitSectorParams, RecoveryDeclaration, ReportConsensusFaultParams, SectorOnChainInfo, - SectorPreCommitInfo, SectorPreCommitOnChainInfo, Sectors, State, SubmitWindowedPoStParams, - TerminateSectorsParams, TerminationDeclaration, VestingFunds, WindowedPoSt, - WithdrawBalanceParams, WithdrawBalanceReturn, CRON_EVENT_PROVING_DEADLINE, - SECTORS_AMT_BITWIDTH, + MinerInfo, Partition, PendingBeneficiaryChange, PieceActivationManifest, PieceChange, + PieceReturn, PoStPartition, PowerPair, PreCommitSectorBatchParams, PreCommitSectorBatchParams2, + PreCommitSectorParams, ProveCommitSectorParams, ProveReplicaUpdates3Params, + ProveReplicaUpdates3Return, RecoveryDeclaration, ReportConsensusFaultParams, SectorChanges, + SectorContentChangedParams, SectorContentChangedReturn, SectorOnChainInfo, SectorPreCommitInfo, + SectorPreCommitOnChainInfo, SectorReturn, SectorUpdateManifest, Sectors, State, + SubmitWindowedPoStParams, TerminateSectorsParams, TerminationDeclaration, VestingFunds, + WindowedPoSt, WithdrawBalanceParams, WithdrawBalanceReturn, CRON_EVENT_PROVING_DEADLINE, + SECTORS_AMT_BITWIDTH, SECTOR_CONTENT_CHANGED, }; use fil_actor_miner::{Method as MinerMethod, ProveCommitAggregateParams}; use fil_actor_power::{ CurrentTotalPowerReturn, EnrollCronEventParams, Method as PowerMethod, UpdateClaimedPowerParams, }; use fil_actor_reward::{Method as RewardMethod, ThisEpochRewardReturn}; +use fil_actor_verifreg::Method as VerifregMethod; use fil_actors_runtime::cbor::serialize; use fil_actors_runtime::runtime::{DomainSeparationTag, Runtime, RuntimePolicy}; use fil_actors_runtime::{test_utils::*, BatchReturn, BatchReturnGen}; @@ -535,7 +542,7 @@ impl ActorHarness { challenge: ChainEpoch, expiration: ChainEpoch, sector_deal_ids: Vec, - unsealed_cid: Option, + unsealed_cid: CompactCommD, ) -> SectorPreCommitInfo { SectorPreCommitInfo { seal_proof: self.seal_proof_type, @@ -544,7 +551,7 @@ impl ActorHarness { seal_rand_epoch: challenge, deal_ids: sector_deal_ids, expiration, - unsealed_cid: CompactCommD::new(unsealed_cid), + unsealed_cid, } } @@ -574,22 +581,13 @@ impl ActorHarness { }) .collect(); - return self.pre_commit_sector_batch_v2( - rt, - &v2, - Method::PreCommitSectorBatch2 as u64, - PreCommitSectorBatchParams2 { sectors: v2.clone() }, - conf.first_for_miner, - base_fee, - ); + return self.pre_commit_sector_batch_v2(rt, &v2, conf.first_for_miner, base_fee); } - fn pre_commit_sector_batch_v2( + pub fn pre_commit_sector_batch_v2( &self, rt: &MockRuntime, sectors: &[SectorPreCommitInfo], - method: MethodNum, - param: impl Serialize, first_for_miner: bool, base_fee: &TokenAmount, ) -> Result, ActorError> { @@ -662,7 +660,11 @@ impl ActorHarness { ); } - let result = rt.call::(method as u64, IpldBlock::serialize_cbor(¶m).unwrap()); + let param = PreCommitSectorBatchParams2 { sectors: sectors.into() }; + let result = rt.call::( + Method::PreCommitSectorBatch2 as u64, + IpldBlock::serialize_cbor(¶m).unwrap(), + ); result } @@ -1110,17 +1112,157 @@ impl ActorHarness { } } - if !expected_pledge.is_zero() { - rt.expect_send_simple( - STORAGE_POWER_ACTOR_ADDR, - PowerMethod::UpdatePledgeTotal as u64, - IpldBlock::serialize_cbor(&expected_pledge).unwrap(), - TokenAmount::zero(), - None, - ExitCode::OK, - ); + expect_update_pledge(rt, &expected_pledge); + } + } + + pub fn prove_replica_updates3_batch( + &self, + rt: &MockRuntime, + sector_updates: &[SectorUpdateManifest], + require_activation_success: bool, + require_notification_success: bool, + ) -> Result { + fn make_proof(i: u8) -> RawBytes { + RawBytes::new(vec![i, i, i, i]) + } + + let params = ProveReplicaUpdates3Params { + sector_updates: sector_updates.into(), + sector_proofs: sector_updates.iter().map(|su| make_proof(su.sector as u8)).collect(), + aggregate_proof: RawBytes::default(), + update_proofs_type: self.seal_proof_type.registered_update_proof().unwrap(), + aggregate_proof_type: RegisteredAggregateProof::Invalid(0), + require_activation_success, + require_notification_success, + }; + rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, self.worker); + rt.expect_validate_caller_addr(self.caller_addrs()); + + 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 { + let sector = self.get_sector(rt, sup.sector); + let unsealed_cid = expect_compute_unsealed_cid_from_pieces( + rt, + sup.sector, + self.seal_proof_type, + &sup.pieces, + ); + rt.expect_replica_verify( + ReplicaUpdateInfo { + update_proof_type: self.seal_proof_type.registered_update_proof().unwrap(), + new_sealed_cid: sup.new_sealed_cid, + old_sealed_cid: sector.sealed_cid, + new_unsealed_cid: unsealed_cid, + proof: make_proof(sup.sector as u8).into(), + }, + Ok(()), + ); + let claims = SectorAllocationClaims { + sector: sup.sector, + expiry: sector.expiration, + claims: claims_from_pieces(&sup.pieces), + }; + sector_claimed_space.push(SectorClaimSummary { + claimed_space: claims.claims.iter().map(|c| c.size.0).sum::().into(), + }); + sector_allocation_claims.push(claims); + + let notifications = notifications_from_pieces(&sup.pieces); + if !notifications.is_empty() { + expected_sector_notifications.push(SectorChanges { + sector: sup.sector, + minimum_commitment_epoch: sector.expiration, + added: notifications, + }); + } + + let duration = sector.expiration - *rt.epoch.borrow(); + let mut deal_size = DealWeight::zero(); + let mut verified_size = DealWeight::zero(); + for piece in &sup.pieces { + if piece.verified_allocation_key.is_some() { + verified_size += piece.size.0 * duration as u64; + } else { + deal_size += piece.size.0 * duration as u64; + } } + + let qa_power_delta = + qa_power_for_weight(self.sector_size, duration, &deal_size, &verified_size) + - qa_power_for_sector(self.sector_size, §or); + expected_qa_power += &qa_power_delta; + let pledge = initial_pledge_for_power( + &qa_power_delta, + &self.baseline_power, + &self.epoch_reward_smooth, + &self.epoch_qa_power_smooth, + &rt.total_fil_circ_supply(), + ); + expected_pledge += pledge; } + + // Expect claiming of verified space for each piece that specified an allocation ID. + if !sector_allocation_claims.iter().all(|sector| sector.claims.is_empty()) { + rt.expect_send_simple( + VERIFIED_REGISTRY_ACTOR_ADDR, + VerifregMethod::ClaimAllocations as u64, + IpldBlock::serialize_cbor(&ClaimAllocationsParams { + sectors: sector_allocation_claims, + all_or_nothing: require_activation_success, + }) + .unwrap(), + TokenAmount::zero(), + IpldBlock::serialize_cbor(&ClaimAllocationsReturn { + sector_results: BatchReturn::ok(sector_updates.len() as u32), + sector_claims: sector_claimed_space, + }) + .unwrap(), + ExitCode::OK, + ); + } + + // Expect pledge & power updates. + self.expect_query_network_info(rt); + expect_update_pledge(rt, &expected_pledge); + expect_update_power(rt, PowerPair::new(BigInt::zero(), expected_qa_power)); + + // Expect SectorContentChanged notification to market. + let sector_notification_resps = expected_sector_notifications + .iter() + .map(|sn| SectorReturn { added: vec![PieceReturn { accepted: true }; sn.added.len()] }) + .collect(); + rt.expect_send_simple( + STORAGE_MARKET_ACTOR_ADDR, + SECTOR_CONTENT_CHANGED, + IpldBlock::serialize_cbor(&SectorContentChangedParams { + sectors: expected_sector_notifications, + }) + .unwrap(), + TokenAmount::zero(), + IpldBlock::serialize_cbor(&SectorContentChangedReturn { + sectors: sector_notification_resps, + }) + .unwrap(), + ExitCode::OK, + ); + + let result: ProveReplicaUpdates3Return = rt + .call::( + MinerMethod::ProveReplicaUpdates3 as u64, + IpldBlock::serialize_cbor(¶ms).unwrap(), + ) + .unwrap() + .unwrap() + .deserialize() + .unwrap(); + assert_eq!(sector_updates.len(), result.activation_results.size()); + // rt.verify(); + Ok(result) } pub fn get_sector(&self, rt: &MockRuntime, sector_number: SectorNumber) -> SectorOnChainInfo { @@ -1177,21 +1319,7 @@ impl ActorHarness { let mut power_delta = PowerPair::zero(); power_delta += &cfg.detected_faults_power_delta.unwrap_or_else(PowerPair::zero); power_delta += &cfg.expired_sectors_power_delta.unwrap_or_else(PowerPair::zero); - - if !power_delta.is_zero() { - let params = UpdateClaimedPowerParams { - raw_byte_delta: power_delta.raw, - quality_adjusted_delta: power_delta.qa, - }; - rt.expect_send_simple( - STORAGE_POWER_ACTOR_ADDR, - PowerMethod::UpdateClaimedPower as u64, - IpldBlock::serialize_cbor(¶ms).unwrap(), - TokenAmount::zero(), - None, - ExitCode::OK, - ); - } + expect_update_power(rt, power_delta); let mut penalty_total = TokenAmount::zero(); let mut pledge_delta = TokenAmount::zero(); @@ -1224,16 +1352,7 @@ impl ActorHarness { pledge_delta += cfg.expired_sectors_pledge_delta; pledge_delta -= immediately_vesting_funds(rt, &state); - if !pledge_delta.is_zero() { - rt.expect_send_simple( - STORAGE_POWER_ACTOR_ADDR, - PowerMethod::UpdatePledgeTotal as u64, - IpldBlock::serialize_cbor(&pledge_delta).unwrap(), - TokenAmount::zero(), - None, - ExitCode::OK, - ); - } + expect_update_pledge(rt, &pledge_delta); // Re-enrollment for next period. if !cfg.no_enrollment { @@ -1353,20 +1472,7 @@ impl ActorHarness { } if let Some(power_delta) = cfg.expected_power_delta { - if !power_delta.is_zero() { - let claim = UpdateClaimedPowerParams { - raw_byte_delta: power_delta.raw, - quality_adjusted_delta: power_delta.qa, - }; - rt.expect_send_simple( - STORAGE_POWER_ACTOR_ADDR, - PowerMethod::UpdateClaimedPower as u64, - IpldBlock::serialize_cbor(&claim).unwrap(), - TokenAmount::zero(), - None, - ExitCode::OK, - ); - } + expect_update_power(rt, power_delta); } rt.call::( @@ -1465,18 +1571,7 @@ impl ActorHarness { if dispute_result.expected_power_delta.is_some() { let expected_power_delta = dispute_result.expected_power_delta.unwrap(); - let claim = UpdateClaimedPowerParams { - raw_byte_delta: expected_power_delta.raw, - quality_adjusted_delta: expected_power_delta.qa, - }; - rt.expect_send_simple( - STORAGE_POWER_ACTOR_ADDR, - PowerMethod::UpdateClaimedPower as u64, - IpldBlock::serialize_cbor(&claim).unwrap(), - TokenAmount::zero(), - None, - ExitCode::OK, - ); + expect_update_power(rt, expected_power_delta); } if dispute_result.expected_reward.is_some() { @@ -1505,14 +1600,7 @@ impl ActorHarness { if dispute_result.expected_pledge_delta.is_some() { let expected_pledge_delta = dispute_result.expected_pledge_delta.unwrap(); - rt.expect_send_simple( - STORAGE_POWER_ACTOR_ADDR, - PowerMethod::UpdatePledgeTotal as u64, - IpldBlock::serialize_cbor(&expected_pledge_delta).unwrap(), - TokenAmount::zero(), - None, - ExitCode::OK, - ); + expect_update_pledge(rt, &expected_pledge_delta); } } @@ -1573,15 +1661,7 @@ impl ActorHarness { rt.set_caller(*REWARD_ACTOR_CODE_ID, REWARD_ACTOR_ADDR); rt.expect_validate_caller_addr(vec![REWARD_ACTOR_ADDR]); - // expect pledge update - rt.expect_send_simple( - STORAGE_POWER_ACTOR_ADDR, - PowerMethod::UpdatePledgeTotal as u64, - IpldBlock::serialize_cbor(&pledge_delta).unwrap(), - TokenAmount::zero(), - None, - ExitCode::OK, - ); + expect_update_pledge(rt, &pledge_delta); if penalty.is_positive() { rt.expect_send_simple( @@ -1720,23 +1800,8 @@ impl ActorHarness { rt.expect_validate_caller_addr(self.caller_addrs()); let ss = fault_sector_infos[0].seal_proof.sector_size().unwrap(); - let expected_delta = power_for_sectors(ss, fault_sector_infos); - let expected_raw_delta = -expected_delta.raw; - let expected_qa_delta = -expected_delta.qa; - - // expect power update - let claim = UpdateClaimedPowerParams { - raw_byte_delta: expected_raw_delta.clone(), - quality_adjusted_delta: expected_qa_delta.clone(), - }; - rt.expect_send_simple( - STORAGE_POWER_ACTOR_ADDR, - PowerMethod::UpdateClaimedPower as u64, - IpldBlock::serialize_cbor(&claim).unwrap(), - TokenAmount::zero(), - None, - ExitCode::OK, - ); + let expected_delta = power_for_sectors(ss, fault_sector_infos).neg(); + expect_update_power(rt, expected_delta.clone()); // Calculate params from faulted sector infos let state = self.get_state(rt); @@ -1745,7 +1810,7 @@ impl ActorHarness { .unwrap(); rt.verify(); - PowerPair { raw: expected_raw_delta, qa: expected_qa_delta } + expected_delta } pub fn declare_recoveries( @@ -1977,16 +2042,7 @@ impl ActorHarness { } } - if !pledge_delta.is_zero() { - rt.expect_send_simple( - STORAGE_POWER_ACTOR_ADDR, - UPDATE_PLEDGE_TOTAL_METHOD, - IpldBlock::serialize_cbor(&pledge_delta).unwrap(), - TokenAmount::zero(), - None, - ExitCode::OK, - ); - } + expect_update_pledge(rt, &pledge_delta); if !sectors_with_data.is_empty() { rt.expect_send_simple( @@ -2004,18 +2060,7 @@ impl ActorHarness { } let sector_power = power_for_sectors(self.sector_size, §or_infos); - let params = UpdateClaimedPowerParams { - raw_byte_delta: -sector_power.raw.clone(), - quality_adjusted_delta: -sector_power.qa.clone(), - }; - rt.expect_send_simple( - STORAGE_POWER_ACTOR_ADDR, - UPDATE_CLAIMED_POWER_METHOD, - IpldBlock::serialize_cbor(¶ms).unwrap(), - TokenAmount::zero(), - None, - ExitCode::OK, - ); + expect_update_power(rt, sector_power.clone().neg()); // create declarations let state: State = rt.get_state(); @@ -2041,7 +2086,7 @@ impl ActorHarness { .unwrap(); rt.verify(); - (-sector_power, pledge_delta) + (sector_power.neg(), pledge_delta) } pub fn change_peer_id(&self, rt: &MockRuntime, new_id: Vec) { @@ -2080,14 +2125,7 @@ impl ActorHarness { rt.set_received(value.clone()); if expected_repaid_from_vest > &TokenAmount::zero() { let pledge_delta = expected_repaid_from_vest.neg(); - rt.expect_send_simple( - STORAGE_POWER_ACTOR_ADDR, - PowerMethod::UpdatePledgeTotal as u64, - IpldBlock::serialize_cbor(&pledge_delta).unwrap(), - TokenAmount::zero(), - None, - ExitCode::OK, - ); + expect_update_pledge(rt, &pledge_delta); } let total_repaid = expected_repaid_from_vest + expected_repaid_from_balance; @@ -2342,20 +2380,7 @@ impl ActorHarness { } } - if !qa_delta.is_zero() { - let params = UpdateClaimedPowerParams { - raw_byte_delta: BigInt::zero(), - quality_adjusted_delta: qa_delta, - }; - rt.expect_send_simple( - STORAGE_POWER_ACTOR_ADDR, - UPDATE_CLAIMED_POWER_METHOD, - IpldBlock::serialize_cbor(¶ms).unwrap(), - TokenAmount::zero(), - None, - ExitCode::OK, - ); - } + expect_update_power(rt, PowerPair::new(BigInt::zero(), qa_delta)); let ret = rt.call::( Method::ExtendSectorExpiration as u64, @@ -2447,20 +2472,7 @@ impl ActorHarness { } } - if !qa_delta.is_zero() { - let params = UpdateClaimedPowerParams { - raw_byte_delta: BigInt::zero(), - quality_adjusted_delta: qa_delta, - }; - rt.expect_send_simple( - STORAGE_POWER_ACTOR_ADDR, - UPDATE_CLAIMED_POWER_METHOD, - IpldBlock::serialize_cbor(¶ms).unwrap(), - TokenAmount::zero(), - None, - ExitCode::OK, - ); - } + expect_update_power(rt, PowerPair::new(BigInt::zero(), qa_delta)); let ret = rt.call::( Method::ExtendSectorExpiration2 as u64, @@ -2805,6 +2817,36 @@ fn make_piece_cid(input: &[u8]) -> Cid { Cid::new_v1(FIL_COMMITMENT_UNSEALED, h) } +// XXX make_peice_manifest + +pub fn claims_from_pieces(pieces: &[PieceActivationManifest]) -> Vec { + pieces + .iter() + .filter_map(|p| { + p.verified_allocation_key.as_ref().map(|a| AllocationClaim { + client: a.client, + allocation_id: a.id, + data: p.cid, + size: p.size, + }) + }) + .collect() +} + +pub fn notifications_from_pieces(pieces: &[PieceActivationManifest]) -> Vec { + pieces + .iter() + .map(|p| { + p.notify.iter().map(|n| PieceChange { + data: p.cid, + size: p.size, + payload: n.payload.clone(), + }) + }) + .flatten() + .collect() +} + 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() } @@ -2939,6 +2981,54 @@ pub fn sectors_as_map(sectors: &[SectorOnChainInfo]) -> SectorsMap { sectors.iter().map(|sector| (sector.sector_number, sector.to_owned())).collect() } +fn expect_compute_unsealed_cid_from_pieces( + rt: &MockRuntime, + sno: SectorNumber, + seal_proof_type: RegisteredSealProof, + pieces: &[PieceActivationManifest], +) -> 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()); + rt.expect_compute_unsealed_sector_cid( + seal_proof_type, + expected_unsealed_cid_inputs, + unsealed_cid.clone(), + ExitCode::OK, + ); + unsealed_cid +} + +fn expect_update_pledge(rt: &MockRuntime, pledge_delta: &TokenAmount) { + if !pledge_delta.is_zero() { + rt.expect_send_simple( + STORAGE_POWER_ACTOR_ADDR, + PowerMethod::UpdatePledgeTotal as u64, + IpldBlock::serialize_cbor(&pledge_delta).unwrap(), + TokenAmount::zero(), + None, + ExitCode::OK, + ); + } +} + +fn expect_update_power(rt: &MockRuntime, delta: PowerPair) { + if !(delta.is_zero()) { + rt.expect_send_simple( + STORAGE_POWER_ACTOR_ADDR, + UPDATE_CLAIMED_POWER_METHOD, + IpldBlock::serialize_cbor(&UpdateClaimedPowerParams { + raw_byte_delta: delta.raw, + quality_adjusted_delta: delta.qa, + }) + .unwrap(), + TokenAmount::zero(), + None, + ExitCode::OK, + ); + } +} + // Helper type for validating deadline state. // // All methods take the state by value so one can (and should) construct a diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index 57f0c766e2..6a39af22c6 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -201,7 +201,7 @@ pub struct Expectations { pub expect_get_randomness_beacon: VecDeque, pub expect_batch_verify_seals: Option, pub expect_aggregate_verify_seals: Option, - pub expect_replica_verify: Option, + pub expect_replica_verify: VecDeque, pub expect_gas_charge: VecDeque, pub expect_gas_available: VecDeque, pub expect_emitted_events: VecDeque, @@ -210,14 +210,21 @@ pub struct Expectations { impl Expectations { fn reset(&mut self) { + // Set skip_verification_on_drop to true to avoid verification in the drop handler + // for the overwritten value. self.skip_verification_on_drop = true; + // This resets skip_verifications_on_drop to default false for a subsequent drop. *self = Default::default(); } fn verify(&mut self) { - // If we don't reset them, we'll try to re-verify on drop. If something fails, we'll panic - // twice and abort making the tests difficult to debug. + // Set skip_verification_on_drop to true to avoid verification in the drop handler + // for the overwritten value. self.skip_verification_on_drop = true; + // Copy expectations into a local and reset self to default values. + // This will trigger Drop on self, which will call back into verify() unless + // marked as skip_verification_on_drop. + // The default values left behind in self will be reset to not skip verification. let this = std::mem::take(self); assert!(!this.expect_validate_caller_any, "expected ValidateCallerAny, not received"); @@ -238,7 +245,7 @@ impl Expectations { ); assert!( this.expect_sends.is_empty(), - "expected all message to be send, unsent messages {:?}", + "expected send {:?}, not received", this.expect_sends ); assert!( @@ -297,7 +304,7 @@ impl Expectations { this.expect_aggregate_verify_seals ); assert!( - this.expect_replica_verify.is_none(), + this.expect_replica_verify.is_empty(), "expect_replica_verify {:?}, not received", this.expect_replica_verify ); @@ -781,7 +788,7 @@ impl MockRuntime { #[allow(dead_code)] pub fn expect_replica_verify(&self, input: ReplicaUpdateInfo, result: anyhow::Result<()>) { let a = ExpectReplicaVerify { input, result }; - self.expectations.borrow_mut().expect_replica_verify = Some(a); + self.expectations.borrow_mut().expect_replica_verify.push_back(a); } #[allow(dead_code)] @@ -1150,28 +1157,28 @@ impl Runtime for MockRuntime { assert_eq!(expected_msg.to, *to, "expected message to {}, was {}", expected_msg.to, to); assert_eq!( expected_msg.method, method, - "expected method {}, was {}", - expected_msg.method, method + "send to {} expected method {}, was {}", + to, expected_msg.method, method ); assert_eq!( expected_msg.params, params, - "expected params {:?}, was {:?}", - expected_msg.params, params, + "send to {}:{} expected params {:?}, was {:?}", + to, method, expected_msg.params, params, ); assert_eq!( expected_msg.value, value, - "expected value {:?}, was {:?}", - expected_msg.value, value, + "send to {}:{} expected value {:?}, was {:?}", + to, method, expected_msg.value, value, ); assert_eq!( expected_msg.gas_limit, gas_limit, - "expected gas limit {:?}, was {:?}", - expected_msg.gas_limit, gas_limit + "send to {}:{} expected gas limit {:?}, was {:?}", + to, method, expected_msg.gas_limit, gas_limit ); assert_eq!( expected_msg.send_flags, send_flags, - "expected send flags {:?}, was {:?}", - expected_msg.send_flags, send_flags + "send to {}:{} expected send flags {:?}, was {:?}", + to, method, expected_msg.send_flags, send_flags ); if let Some(e) = expected_msg.send_error { @@ -1525,7 +1532,7 @@ impl Verifier for MockRuntime { .expectations .borrow_mut() .expect_replica_verify - .take() + .pop_front() .expect("unexpected call to verify replica update"); assert_eq!(exp.input.update_proof_type, replica.update_proof_type, "mismatched proof type"); assert_eq!(exp.input.new_sealed_cid, replica.new_sealed_cid, "mismatched new sealed CID");