#![allow(dead_code)] use cid::Cid; use frc46_token::token::types::{TransferFromParams, TransferFromReturn}; use num_traits::{FromPrimitive, Zero}; use regex::Regex; use std::cmp::min; use std::{cell::RefCell, collections::HashMap}; use fil_actor_market::ext::account::{AuthenticateMessageParams, AUTHENTICATE_MESSAGE_METHOD}; use fil_actor_market::ext::verifreg::{AllocationID, AllocationRequest, AllocationsResponse}; use fil_actor_market::{ balance_table::BalanceTable, deal_id_key, ext, ext::miner::GetControlAddressesReturnParams, gen_rand_next_epoch, testing::check_state_invariants, ActivateDealsParams, ActivateDealsResult, Actor as MarketActor, ClientDealProposal, DealArray, DealMetaArray, DealProposal, DealState, Label, Method, OnMinerSectorsTerminateParams, PublishStorageDealsParams, PublishStorageDealsReturn, SectorDeals, State, VerifyDealsForActivationParams, VerifyDealsForActivationReturn, WithdrawBalanceParams, WithdrawBalanceReturn, NO_ALLOCATION_ID, PROPOSALS_AMT_BITWIDTH, }; use fil_actor_power::{CurrentTotalPowerReturn, Method as PowerMethod}; use fil_actor_reward::Method as RewardMethod; use fil_actors_runtime::builtin::HAMT_BIT_WIDTH; use fil_actors_runtime::cbor::serialize; use fil_actors_runtime::{ make_map_with_root_and_bitwidth, network::EPOCHS_IN_DAY, runtime::{builtins::Type, Policy, Runtime}, test_utils::*, ActorError, BatchReturn, SetMultimap, BURNT_FUNDS_ACTOR_ADDR, CALLER_TYPES_SIGNABLE, CRON_ACTOR_ADDR, DATACAP_TOKEN_ACTOR_ADDR, REWARD_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, }; use fvm_ipld_encoding::{to_vec, RawBytes}; use fvm_shared::bigint::BigInt; use fvm_shared::clock::{ChainEpoch, EPOCH_UNDEFINED}; use fvm_shared::crypto::signature::Signature; use fvm_shared::deal::DealID; use fvm_shared::piece::{PaddedPieceSize, PieceInfo}; use fvm_shared::reward::ThisEpochRewardReturn; use fvm_shared::sector::StoragePower; use fvm_shared::smooth::FilterEstimate; use fvm_shared::{ address::Address, econ::TokenAmount, error::ExitCode, METHOD_CONSTRUCTOR, METHOD_SEND, }; // Define common set of actor ids that will be used across all tests. const OWNER_ID: u64 = 101; const PROVIDER_ID: u64 = 102; const WORKER_ID: u64 = 103; const CLIENT_ID: u64 = 104; const CONTROL_ID: u64 = 200; pub const OWNER_ADDR: Address = Address::new_id(OWNER_ID); pub const PROVIDER_ADDR: Address = Address::new_id(PROVIDER_ID); pub const WORKER_ADDR: Address = Address::new_id(WORKER_ID); pub const CLIENT_ADDR: Address = Address::new_id(CLIENT_ID); pub const CONTROL_ADDR: Address = Address::new_id(CONTROL_ID); pub struct MinerAddresses { pub owner: Address, pub worker: Address, pub provider: Address, pub control: Vec
, } // Use the predefined actor addresses by default impl Default for MinerAddresses { fn default() -> Self { MinerAddresses { owner: OWNER_ADDR, worker: WORKER_ADDR, provider: PROVIDER_ADDR, control: vec![CONTROL_ADDR], } } } pub fn setup() -> MockRuntime { let actor_code_cids = HashMap::from([ (OWNER_ADDR, *ACCOUNT_ACTOR_CODE_ID), (WORKER_ADDR, *ACCOUNT_ACTOR_CODE_ID), (PROVIDER_ADDR, *MINER_ACTOR_CODE_ID), (CLIENT_ADDR, *ACCOUNT_ACTOR_CODE_ID), ]); let mut rt = MockRuntime { receiver: STORAGE_MARKET_ACTOR_ADDR, caller: SYSTEM_ACTOR_ADDR, caller_type: *INIT_ACTOR_CODE_ID, actor_code_cids, balance: RefCell::new(TokenAmount::from_whole(10)), ..Default::default() }; construct_and_verify(&mut rt); rt } /// Checks internal invariants of market state asserting none of them are broken. pub fn check_state(rt: &MockRuntime) { let (_, acc) = check_state_invariants(&rt.get_state::(), rt.store(), &rt.get_balance(), rt.epoch); acc.assert_empty(); } /// Checks state, allowing expected invariants to fail. The invariants *must* fail in the /// provided order. pub fn check_state_with_expected(rt: &MockRuntime, expected_patterns: &[Regex]) { let (_, acc) = check_state_invariants(&rt.get_state::(), rt.store(), &rt.get_balance(), rt.epoch); acc.assert_expected(expected_patterns); } pub fn construct_and_verify(rt: &mut MockRuntime) { rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); assert_eq!( RawBytes::default(), rt.call::(METHOD_CONSTRUCTOR, &RawBytes::default()).unwrap() ); rt.verify(); } pub fn get_escrow_balance(rt: &MockRuntime, addr: &Address) -> Result { let st: State = rt.get_state(); let et = BalanceTable::from_root(rt.store(), &st.escrow_table) .expect("failed to construct balance table from blockstore"); Ok(et.get(addr).expect("address does not exist in escrow balance table")) } pub fn expect_get_control_addresses( rt: &mut MockRuntime, provider: Address, owner: Address, worker: Address, controls: Vec
, ) { let result = GetControlAddressesReturnParams { owner, worker, control_addresses: controls }; rt.expect_send( provider, ext::miner::CONTROL_ADDRESSES_METHOD, RawBytes::default(), TokenAmount::zero(), RawBytes::serialize(result).unwrap(), ExitCode::OK, ) } pub fn expect_provider_control_address( rt: &mut MockRuntime, provider: Address, owner: Address, worker: Address, ) { expect_get_control_addresses(rt, provider, owner, worker, vec![]) } pub fn add_provider_funds(rt: &mut MockRuntime, amount: TokenAmount, addrs: &MinerAddresses) { rt.set_value(amount.clone()); rt.set_address_actor_type(addrs.provider, *MINER_ACTOR_CODE_ID); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, addrs.owner); rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); expect_provider_control_address(rt, addrs.provider, addrs.owner, addrs.worker); assert_eq!( RawBytes::default(), rt.call::( Method::AddBalance as u64, &RawBytes::serialize(addrs.provider).unwrap(), ) .unwrap() ); rt.verify(); rt.add_balance(amount); } pub fn add_participant_funds(rt: &mut MockRuntime, addr: Address, amount: TokenAmount) { rt.set_value(amount.clone()); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, addr); rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); assert!(rt .call::(Method::AddBalance as u64, &RawBytes::serialize(addr).unwrap()) .is_ok()); rt.verify(); rt.add_balance(amount); } pub fn withdraw_provider_balance( rt: &mut MockRuntime, withdraw_amount: TokenAmount, expected_send: TokenAmount, provider: Address, owner: Address, worker: Address, ) { rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, worker); rt.expect_validate_caller_addr(vec![owner, worker]); expect_provider_control_address(rt, provider, owner, worker); let params = WithdrawBalanceParams { provider_or_client: provider, amount: withdraw_amount }; rt.expect_send( owner, METHOD_SEND, RawBytes::default(), expected_send.clone(), RawBytes::default(), ExitCode::OK, ); let ret: WithdrawBalanceReturn = rt .call::(Method::WithdrawBalance as u64, &RawBytes::serialize(params).unwrap()) .unwrap() .deserialize() .unwrap(); rt.verify(); assert_eq!( expected_send, ret.amount_withdrawn, "return value indicates {} withdrawn but expected {}", ret.amount_withdrawn, expected_send ); } pub fn withdraw_client_balance( rt: &mut MockRuntime, withdraw_amount: TokenAmount, expected_send: TokenAmount, client: Address, ) { rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, client); rt.expect_send( client, METHOD_SEND, RawBytes::default(), expected_send.clone(), RawBytes::default(), ExitCode::OK, ); rt.expect_validate_caller_addr(vec![client]); let params = WithdrawBalanceParams { provider_or_client: client, amount: withdraw_amount }; let ret: WithdrawBalanceReturn = rt .call::(Method::WithdrawBalance as u64, &RawBytes::serialize(params).unwrap()) .unwrap() .deserialize() .unwrap(); rt.verify(); assert_eq!( expected_send, ret.amount_withdrawn, "return value indicates {} withdrawn but expected {}", ret.amount_withdrawn, expected_send ); } pub fn activate_deals( rt: &mut MockRuntime, sector_expiry: ChainEpoch, provider: Address, current_epoch: ChainEpoch, deal_ids: &[DealID], ) -> ActivateDealsResult { let ret = activate_deals_raw(rt, sector_expiry, provider, current_epoch, deal_ids).unwrap(); ret.deserialize().expect("VerifyDealsForActivation failed!") } pub fn activate_deals_raw( rt: &mut MockRuntime, sector_expiry: ChainEpoch, provider: Address, current_epoch: ChainEpoch, deal_ids: &[DealID], ) -> Result { rt.set_epoch(current_epoch); rt.set_caller(*MINER_ACTOR_CODE_ID, provider); rt.expect_validate_caller_type(vec![Type::Miner]); let params = ActivateDealsParams { deal_ids: deal_ids.to_vec(), sector_expiry }; let ret = rt .call::(Method::ActivateDeals as u64, &RawBytes::serialize(params).unwrap())?; rt.verify(); for d in deal_ids { let s = get_deal_state(rt, *d); assert_eq!(current_epoch, s.sector_start_epoch); } Ok(ret) } pub fn get_deal_proposal(rt: &mut MockRuntime, deal_id: DealID) -> DealProposal { let st: State = rt.get_state(); let deals = DealArray::load(&st.proposals, &rt.store).unwrap(); let d = deals.get(deal_id).unwrap(); d.unwrap().clone() } pub fn get_pending_deal_allocation(rt: &mut MockRuntime, deal_id: DealID) -> AllocationID { let st: State = rt.get_state(); let pending_allocations = make_map_with_root_and_bitwidth(&st.pending_deal_allocation_ids, &rt.store, HAMT_BIT_WIDTH) .unwrap(); *pending_allocations.get(&deal_id_key(deal_id)).unwrap().unwrap_or(&NO_ALLOCATION_ID) } pub fn get_locked_balance(rt: &mut MockRuntime, addr: Address) -> TokenAmount { let st: State = rt.get_state(); let lt = BalanceTable::from_root(&rt.store, &st.locked_table).unwrap(); lt.get(&addr).unwrap() } pub fn get_deal_state(rt: &mut MockRuntime, deal_id: DealID) -> DealState { let st: State = rt.get_state(); let states = DealMetaArray::load(&st.states, &rt.store).unwrap(); let s = states.get(deal_id).unwrap(); *s.unwrap() } pub fn update_last_updated(rt: &mut MockRuntime, deal_id: DealID, new_last_updated: ChainEpoch) { let st: State = rt.get_state(); let mut states = DealMetaArray::load(&st.states, &rt.store).unwrap(); let s = *states.get(deal_id).unwrap().unwrap(); states.set(deal_id, DealState { last_updated_epoch: new_last_updated, ..s }).unwrap(); let root = states.flush().unwrap(); rt.replace_state(&State { states: root, ..st }) } pub fn delete_deal_proposal(rt: &mut MockRuntime, deal_id: DealID) { let mut st: State = rt.get_state(); let mut deals = DealArray::load(&st.proposals, &rt.store).unwrap(); deals.delete(deal_id).unwrap(); let root = deals.flush().unwrap(); st.proposals = root; rt.replace_state(&st) } // if this is the first crontick for the deal, it's next tick will be scheduled at `desiredNextEpoch` // if this is not the first crontick, the `desiredNextEpoch` param is ignored. pub fn cron_tick_and_assert_balances( rt: &mut MockRuntime, client_addr: Address, provider_addr: Address, current_epoch: ChainEpoch, deal_id: DealID, ) -> (TokenAmount, TokenAmount) { // fetch current client and provider escrow balances let c_locked = get_locked_balance(rt, client_addr); let c_escrow = get_escrow_balance(rt, &client_addr).unwrap(); let p_locked = get_locked_balance(rt, provider_addr); let p_escrow = get_escrow_balance(rt, &provider_addr).unwrap(); let mut amount_slashed = TokenAmount::zero(); let s = get_deal_state(rt, deal_id); let d = get_deal_proposal(rt, deal_id); // end epoch for payment calc let mut payment_end = d.end_epoch; if s.slash_epoch != EPOCH_UNDEFINED { rt.expect_send( BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), d.provider_collateral.clone(), RawBytes::default(), ExitCode::OK, ); amount_slashed = d.provider_collateral; if s.slash_epoch < d.start_epoch { payment_end = d.start_epoch; } else { payment_end = s.slash_epoch; } } else if current_epoch < payment_end { payment_end = current_epoch; } // start epoch for payment calc let mut payment_start = d.start_epoch; if s.last_updated_epoch != EPOCH_UNDEFINED { payment_start = s.last_updated_epoch; } let duration = payment_end - payment_start; let payment = duration * d.storage_price_per_epoch; // expected updated amounts let updated_client_escrow = c_escrow - &payment; let updated_provider_escrow = (p_escrow + &payment) - &amount_slashed; let mut updated_client_locked = c_locked - &payment; let mut updated_provider_locked = p_locked; // if the deal has expired or been slashed, locked amount will be zero for provider and client. let is_deal_expired = payment_end == d.end_epoch; if is_deal_expired || s.slash_epoch != EPOCH_UNDEFINED { updated_client_locked = TokenAmount::zero(); updated_provider_locked = TokenAmount::zero(); } cron_tick(rt); assert_eq!(updated_client_escrow, get_escrow_balance(rt, &client_addr).unwrap()); assert_eq!(updated_client_locked, get_locked_balance(rt, client_addr)); assert_eq!(updated_provider_escrow, get_escrow_balance(rt, &provider_addr).unwrap()); assert_eq!(updated_provider_locked, get_locked_balance(rt, provider_addr)); (payment, amount_slashed) } pub fn cron_tick_no_change(rt: &mut MockRuntime, client_addr: Address, provider_addr: Address) { let st: State = rt.get_state(); let epoch_cid = st.deal_ops_by_epoch; // fetch current client and provider escrow balances let c_locked = get_locked_balance(rt, client_addr); let c_escrow = get_escrow_balance(rt, &client_addr).unwrap(); let p_locked = get_locked_balance(rt, provider_addr); let p_escrow = get_escrow_balance(rt, &provider_addr).unwrap(); cron_tick(rt); let st: State = rt.get_state(); assert_eq!(epoch_cid, st.deal_ops_by_epoch); assert_eq!(c_locked, get_locked_balance(rt, client_addr)); assert_eq!(c_escrow, get_escrow_balance(rt, &client_addr).unwrap()); assert_eq!(p_locked, get_locked_balance(rt, provider_addr)); assert_eq!(p_escrow, get_escrow_balance(rt, &provider_addr).unwrap()); } pub fn publish_deals( rt: &mut MockRuntime, addrs: &MinerAddresses, publish_deals: &[DealProposal], next_allocation_id: AllocationID, ) -> Vec { rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); let return_value = GetControlAddressesReturnParams { owner: addrs.owner, worker: addrs.worker, control_addresses: addrs.control.clone(), }; rt.expect_send( addrs.provider, ext::miner::CONTROL_ADDRESSES_METHOD, RawBytes::default(), TokenAmount::zero(), RawBytes::serialize(return_value).unwrap(), ExitCode::OK, ); expect_query_network_info(rt); let mut params: PublishStorageDealsParams = PublishStorageDealsParams { deals: vec![] }; let mut alloc_id = next_allocation_id; for deal in publish_deals { // create a client proposal with a valid signature let buf = RawBytes::serialize(deal.clone()).expect("failed to marshal deal proposal"); let sig = Signature::new_bls("does not matter".as_bytes().to_vec()); let client_proposal = ClientDealProposal { proposal: deal.clone(), client_signature: sig.clone() }; params.deals.push(client_proposal); // expect an invocation of authenticate_message to verify the above signature let param = RawBytes::serialize(AuthenticateMessageParams { signature: "does not matter".as_bytes().to_vec(), message: buf.to_vec(), }) .unwrap(); rt.expect_send( deal.client, ext::account::AUTHENTICATE_MESSAGE_METHOD as u64, param, TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); if deal.verified_deal { // Expect transfer of data cap to the verified registry, with spec for the allocation. let curr_epoch = rt.epoch; let alloc_req = ext::verifreg::AllocationRequests { allocations: vec![AllocationRequest { provider: deal.provider, data: deal.piece_cid, size: deal.piece_size, term_min: deal.end_epoch - deal.start_epoch, term_max: (deal.end_epoch - deal.start_epoch) + 90 * EPOCHS_IN_DAY, expiration: min(deal.start_epoch, curr_epoch + 60 * EPOCHS_IN_DAY), }], extensions: vec![], }; let datacap_amount = TokenAmount::from_whole(deal.piece_size.0 as i64); let params = TransferFromParams { from: deal.client, to: VERIFIED_REGISTRY_ACTOR_ADDR, amount: datacap_amount.clone(), operator_data: serialize(&alloc_req, "allocation requests").unwrap(), }; let alloc_ids = AllocationsResponse { allocation_results: BatchReturn::ok(1), extension_results: BatchReturn::empty(), new_allocations: vec![alloc_id], }; rt.expect_send( DATACAP_TOKEN_ACTOR_ADDR, ext::datacap::TRANSFER_FROM_METHOD as u64, serialize(¶ms, "transfer from params").unwrap(), TokenAmount::zero(), serialize( &TransferFromReturn { from_balance: TokenAmount::zero(), to_balance: datacap_amount, allowance: TokenAmount::zero(), recipient_data: serialize(&alloc_ids, "allocation response").unwrap(), }, "transfer from return", ) .unwrap(), ExitCode::OK, ); alloc_id += 1 } } let ret: PublishStorageDealsReturn = rt .call::( Method::PublishStorageDeals as u64, &RawBytes::serialize(params).unwrap(), ) .unwrap() .deserialize() .unwrap(); rt.verify(); assert_eq!(ret.ids.len(), publish_deals.len()); // assert state after publishing the deals alloc_id = next_allocation_id; for (i, deal_id) in ret.ids.iter().enumerate() { let expected = &publish_deals[i]; let p = get_deal_proposal(rt, *deal_id); assert_eq!(expected, &p); if p.verified_deal { assert_eq!(get_pending_deal_allocation(rt, *deal_id), alloc_id); alloc_id += 1; } } ret.ids } pub fn publish_deals_expect_abort( rt: &mut MockRuntime, miner_addresses: &MinerAddresses, proposal: DealProposal, expected_exit_code: ExitCode, ) { rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); expect_provider_control_address( rt, miner_addresses.provider, miner_addresses.owner, miner_addresses.worker, ); let deal_serialized = RawBytes::serialize(proposal.clone()).expect("Failed to marshal deal proposal"); let client_signature = Signature::new_bls(deal_serialized.to_vec()); expect_query_network_info(rt); let auth_param = RawBytes::serialize(AuthenticateMessageParams { signature: deal_serialized.to_vec(), message: deal_serialized.to_vec(), }) .unwrap(); rt.expect_send( proposal.client, AUTHENTICATE_MESSAGE_METHOD, auth_param, TokenAmount::zero(), RawBytes::default(), ExitCode::OK, ); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); let deal_params = PublishStorageDealsParams { deals: vec![ClientDealProposal { proposal, client_signature }], }; expect_abort( expected_exit_code, rt.call::( Method::PublishStorageDeals as u64, &RawBytes::serialize(&deal_params).unwrap(), ), ); rt.verify(); } pub fn assert_deals_not_activated(rt: &mut MockRuntime, _epoch: ChainEpoch, deal_ids: &[DealID]) { let st: State = rt.get_state(); let states = DealMetaArray::load(&st.states, &rt.store).unwrap(); for d in deal_ids { let opt = states.get(*d).unwrap(); assert!(opt.is_none()); } } pub fn cron_tick(rt: &mut MockRuntime) { assert_eq!(RawBytes::default(), cron_tick_raw(rt).unwrap()); rt.verify() } pub fn cron_tick_raw(rt: &mut MockRuntime) -> Result { rt.expect_validate_caller_addr(vec![CRON_ACTOR_ADDR]); rt.set_caller(*CRON_ACTOR_CODE_ID, CRON_ACTOR_ADDR); rt.call::(Method::CronTick as u64, &RawBytes::default()) } pub fn expect_query_network_info(rt: &mut MockRuntime) { //networkQAPower //networkBaselinePower let reward = TokenAmount::from_whole(10); let power = StoragePower::from_i128(1 << 50).unwrap(); let epoch_reward_smooth = FilterEstimate::new(reward.atto().clone(), BigInt::from(0u8)); let current_power = CurrentTotalPowerReturn { raw_byte_power: StoragePower::default(), quality_adj_power: power.clone(), pledge_collateral: TokenAmount::default(), quality_adj_power_smoothed: FilterEstimate::new(reward.atto().clone(), BigInt::zero()), }; let current_reward = ThisEpochRewardReturn { this_epoch_baseline_power: power, this_epoch_reward_smoothed: epoch_reward_smooth, }; rt.expect_send( REWARD_ACTOR_ADDR, RewardMethod::ThisEpochReward as u64, RawBytes::default(), TokenAmount::zero(), RawBytes::serialize(current_reward).unwrap(), ExitCode::OK, ); rt.expect_send( STORAGE_POWER_ACTOR_ADDR, PowerMethod::CurrentTotalPower as u64, RawBytes::default(), TokenAmount::zero(), RawBytes::serialize(current_power).unwrap(), ExitCode::OK, ); } pub fn assert_n_good_deals(dobe: &SetMultimap, epoch: ChainEpoch, n: isize) where BS: fvm_ipld_blockstore::Blockstore, { let deal_updates_interval = Policy::default().deal_updates_interval; let mut count = 0; dobe.for_each(epoch, |id| { assert_eq!(epoch % deal_updates_interval, (id as i64) % deal_updates_interval); count += 1; Ok(()) }) .unwrap(); assert_eq!(n, count, "unexpected deal count at epoch {}", epoch); } pub fn assert_deals_terminated(rt: &mut MockRuntime, epoch: ChainEpoch, deal_ids: &[DealID]) { for &deal_id in deal_ids { let s = get_deal_state(rt, deal_id); assert_eq!(s.slash_epoch, epoch); } } pub fn assert_deals_not_terminated(rt: &mut MockRuntime, deal_ids: &[DealID]) { for &deal_id in deal_ids { let s = get_deal_state(rt, deal_id); assert_eq!(s.slash_epoch, EPOCH_UNDEFINED); } } pub fn assert_deal_deleted(rt: &mut MockRuntime, deal_id: DealID, p: DealProposal) { use cid::multihash::Code; use cid::multihash::MultihashDigest; use fil_actors_runtime::Map; use fvm_ipld_hamt::BytesKey; let st: State = rt.get_state(); // Check that the deal_id is not in st.proposals. let deals = DealArray::load(&st.proposals, &rt.store).unwrap(); let d = deals.get(deal_id).unwrap(); assert!(d.is_none()); // Check that the deal_id is not in st.states let states = DealMetaArray::load(&st.states, &rt.store).unwrap(); let s = states.get(deal_id).unwrap(); assert!(s.is_none()); let mh_code = Code::Blake2b256; let p_cid = Cid::new_v1(fvm_ipld_encoding::DAG_CBOR, mh_code.digest(&to_vec(&p).unwrap())); // Check that the deal_id is not in st.pending_proposals. let pending_deals: Map = fil_actors_runtime::make_map_with_root_and_bitwidth::< fvm_ipld_blockstore::MemoryBlockstore, DealProposal, >(&st.pending_proposals, &*rt.store, PROPOSALS_AMT_BITWIDTH) .unwrap(); assert!(!pending_deals.contains_key(&BytesKey(p_cid.to_bytes())).unwrap()); } pub fn assert_deal_failure(add_funds: bool, post_setup: F, exit_code: ExitCode, sig_valid: bool) where F: FnOnce(&mut MockRuntime, &mut DealProposal), { let current_epoch = ChainEpoch::from(5); let start_epoch = 10; let end_epoch = start_epoch + 200 * EPOCHS_IN_DAY; let mut rt = setup(); let mut deal_proposal = if add_funds { generate_deal_and_add_funds( &mut rt, CLIENT_ADDR, &MinerAddresses::default(), start_epoch, end_epoch, ) } else { generate_deal_proposal(CLIENT_ADDR, PROVIDER_ADDR, start_epoch, end_epoch) }; rt.set_epoch(current_epoch); post_setup(&mut rt, &mut deal_proposal); rt.expect_validate_caller_type((*CALLER_TYPES_SIGNABLE).to_vec()); expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); expect_query_network_info(&mut rt); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); let buf = RawBytes::serialize(deal_proposal.clone()).expect("failed to marshal deal proposal"); let sig = Signature::new_bls(buf.to_vec()); let auth_param = RawBytes::serialize(AuthenticateMessageParams { signature: buf.to_vec(), message: buf.to_vec(), }) .unwrap(); rt.expect_send( deal_proposal.client, AUTHENTICATE_MESSAGE_METHOD, auth_param, TokenAmount::zero(), RawBytes::default(), match sig_valid { true => ExitCode::OK, false => ExitCode::USR_ILLEGAL_ARGUMENT, }, ); let params: PublishStorageDealsParams = PublishStorageDealsParams { deals: vec![ClientDealProposal { proposal: deal_proposal, client_signature: sig }], }; assert_eq!( exit_code, rt.call::( Method::PublishStorageDeals as u64, &RawBytes::serialize(params).unwrap(), ) .unwrap_err() .exit_code() ); rt.verify(); check_state(&rt); } pub fn process_epoch(start_epoch: ChainEpoch, deal_id: DealID) -> ChainEpoch { let policy = Policy::default(); gen_rand_next_epoch(&policy, start_epoch, deal_id) } pub fn publish_and_activate_deal( rt: &mut MockRuntime, client: Address, addrs: &MinerAddresses, start_epoch: ChainEpoch, end_epoch: ChainEpoch, current_epoch: ChainEpoch, sector_expiry: ChainEpoch, ) -> DealID { let deal = generate_deal_and_add_funds(rt, client, addrs, start_epoch, end_epoch); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, addrs.worker); let deal_ids = publish_deals(rt, addrs, &[deal], NO_ALLOCATION_ID); // unverified deal activate_deals(rt, sector_expiry, addrs.provider, current_epoch, &deal_ids); deal_ids[0] } pub fn generate_and_publish_deal( rt: &mut MockRuntime, client: Address, addrs: &MinerAddresses, start_epoch: ChainEpoch, end_epoch: ChainEpoch, ) -> DealID { let deal = generate_deal_and_add_funds(rt, client, addrs, start_epoch, end_epoch); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, addrs.worker); let deal_ids = publish_deals(rt, addrs, &[deal], NO_ALLOCATION_ID); // unverified deal deal_ids[0] } pub fn generate_and_publish_verified_deal( rt: &mut MockRuntime, client: Address, addrs: &MinerAddresses, start_epoch: ChainEpoch, end_epoch: ChainEpoch, next_allocation_id: AllocationID, ) -> DealID { let mut deal = generate_deal_and_add_funds(rt, client, addrs, start_epoch, end_epoch); deal.verified_deal = true; rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, addrs.worker); let deal_ids = publish_deals(rt, addrs, &[deal], next_allocation_id); deal_ids[0] } pub fn generate_and_publish_deal_for_piece( rt: &mut MockRuntime, client: Address, addrs: &MinerAddresses, start_epoch: ChainEpoch, end_epoch: ChainEpoch, piece_cid: Cid, piece_size: PaddedPieceSize, ) -> DealID { // generate deal let storage_price_per_epoch = TokenAmount::from_atto(10u8); let client_collateral = TokenAmount::from_atto(10u8); let provider_collateral = TokenAmount::from_atto(10u8); let deal = DealProposal { piece_cid, piece_size, verified_deal: false, client, provider: addrs.provider, label: Label::String("label".to_string()), start_epoch, end_epoch, storage_price_per_epoch, provider_collateral, client_collateral, }; // add funds add_provider_funds(rt, deal.provider_collateral.clone(), addrs); add_participant_funds(rt, client, deal.client_balance_requirement()); // publish rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, addrs.worker); let deal_ids = publish_deals(rt, addrs, &[deal], NO_ALLOCATION_ID); // unverified deal deal_ids[0] } pub fn generate_deal_and_add_funds( rt: &mut MockRuntime, client: Address, addrs: &MinerAddresses, start_epoch: ChainEpoch, end_epoch: ChainEpoch, ) -> DealProposal { let deal = generate_deal_proposal(client, addrs.provider, start_epoch, end_epoch); add_provider_funds(rt, deal.provider_collateral.clone(), addrs); add_participant_funds(rt, client, deal.client_balance_requirement()); deal } pub fn generate_deal_with_collateral_and_add_funds( rt: &mut MockRuntime, client: Address, addrs: &MinerAddresses, provider_collateral: TokenAmount, client_collateral: TokenAmount, start_epoch: ChainEpoch, end_epoch: ChainEpoch, ) -> DealProposal { let deal = generate_deal_proposal_with_collateral( client, addrs.provider, client_collateral, provider_collateral, start_epoch, end_epoch, ); add_provider_funds(rt, deal.provider_collateral.clone(), addrs); add_participant_funds(rt, client, deal.client_balance_requirement()); deal } fn generate_deal_proposal_with_collateral( client: Address, provider: Address, client_collateral: TokenAmount, provider_collateral: TokenAmount, start_epoch: ChainEpoch, end_epoch: ChainEpoch, ) -> DealProposal { let piece_cid = make_piece_cid("1".as_bytes()); let piece_size = PaddedPieceSize(2048u64); let storage_price_per_epoch = TokenAmount::from_atto(10u8); DealProposal { piece_cid, piece_size, verified_deal: false, client, provider, label: Label::String("label".to_string()), start_epoch, end_epoch, storage_price_per_epoch, provider_collateral, client_collateral, } } pub fn generate_deal_proposal( client: Address, provider: Address, start_epoch: ChainEpoch, end_epoch: ChainEpoch, ) -> DealProposal { let client_collateral = TokenAmount::from_atto(10u8); let provider_collateral = TokenAmount::from_atto(10u8); generate_deal_proposal_with_collateral( client, provider, client_collateral, provider_collateral, start_epoch, end_epoch, ) } pub fn terminate_deals(rt: &mut MockRuntime, miner_addr: Address, deal_ids: &[DealID]) { let ret = terminate_deals_raw(rt, miner_addr, deal_ids).unwrap(); assert_eq!(ret, RawBytes::default()); rt.verify(); } pub fn terminate_deals_raw( rt: &mut MockRuntime, miner_addr: Address, deal_ids: &[DealID], ) -> Result { rt.set_caller(*MINER_ACTOR_CODE_ID, miner_addr); rt.expect_validate_caller_type(vec![Type::Miner]); let params = OnMinerSectorsTerminateParams { epoch: rt.epoch, deal_ids: deal_ids.to_vec() }; rt.call::( Method::OnMinerSectorsTerminate as u64, &RawBytes::serialize(params).unwrap(), ) } pub fn assert_account_zero(rt: &mut MockRuntime, addr: Address) { assert!(get_escrow_balance(rt, &addr).unwrap().is_zero()); assert!(get_locked_balance(rt, addr).is_zero()); } pub fn verify_deals_for_activation( rt: &mut MockRuntime, provider: Address, sector_deals: Vec, piece_info_override: F, ) -> VerifyDealsForActivationReturn where F: Fn(usize) -> Option>, { rt.expect_validate_caller_type(vec![Type::Miner]); rt.set_caller(*MINER_ACTOR_CODE_ID, provider); for (i, sd) in sector_deals.iter().enumerate() { let pi = piece_info_override(i).unwrap_or_else(|| { vec![PieceInfo { cid: make_piece_cid("1".as_bytes()), size: PaddedPieceSize(2048) }] }); rt.expect_compute_unsealed_sector_cid( sd.sector_type, pi, make_piece_cid("1".as_bytes()), ExitCode::OK, ) } let param = VerifyDealsForActivationParams { sectors: sector_deals }; let ret: VerifyDealsForActivationReturn = rt .call::( Method::VerifyDealsForActivation as u64, &RawBytes::serialize(param).unwrap(), ) .unwrap() .deserialize() .expect("VerifyDealsForActivation failed!"); rt.verify(); ret }