Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion actors/market/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1091,7 +1091,7 @@ where
.map_err(|e| e.wrap(&format!("cannot activate deal {}", deal_id)))?;

total_deal_space += proposal.piece_size.0;
let deal_space_time = deal_weight(proposal);
let deal_space_time = detail::deal_weight(proposal);
if proposal.verified_deal {
total_verified_space_time += deal_space_time;
} else {
Expand Down
14 changes: 8 additions & 6 deletions actors/market/src/policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,16 @@ use num_traits::Zero;
use super::deal::DealProposal;

pub mod detail {
use super::*;

/// Maximum length of a deal label.
pub const DEAL_MAX_LABEL_SIZE: usize = 256;

/// Computes the weight for a deal proposal, which is a function of its size and duration.
pub fn deal_weight(proposal: &DealProposal) -> DealWeight {
let deal_duration = DealWeight::from(proposal.duration());
deal_duration * proposal.piece_size.0
}
}

/// Bounds (inclusive) on deal duration.
Expand Down Expand Up @@ -66,9 +74,3 @@ pub(super) fn collateral_penalty_for_deal_activation_missed(
) -> TokenAmount {
provider_collateral
}

/// Computes the weight for a deal proposal, which is a function of its size and duration.
pub(super) fn deal_weight(proposal: &DealProposal) -> DealWeight {
let deal_duration = DealWeight::from(proposal.duration());
deal_duration * proposal.piece_size.0
}
38 changes: 37 additions & 1 deletion actors/market/tests/harness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use fil_actor_market::{
balance_table::BalanceTable, ext, ext::miner::GetControlAddressesReturnParams,
gen_rand_next_epoch, ActivateDealsParams, Actor as MarketActor, ClientDealProposal, DealArray,
DealMetaArray, DealProposal, DealState, Method, OnMinerSectorsTerminateParams,
PublishStorageDealsParams, PublishStorageDealsReturn, State, WithdrawBalanceParams,
PublishStorageDealsParams, PublishStorageDealsReturn, SectorDeals, State,
VerifyDealsForActivationParams, VerifyDealsForActivationReturn, WithdrawBalanceParams,
WithdrawBalanceReturn, PROPOSALS_AMT_BITWIDTH,
};
use fil_actor_power::{CurrentTotalPowerReturn, Method as PowerMethod};
Expand Down Expand Up @@ -744,6 +745,20 @@ pub fn generate_and_publish_deal(
deal_ids[0]
}

pub fn generate_and_publish_verified_deal(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name should include the fact that we are adding funds.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's basically a copy of the function above with the verified_deal setting. I'd say we should keep the name or rename the one above as well. What do you think @ZenGround0 ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah my bad, keeping it this way is ok

rt: &mut MockRuntime,
client: Address,
addrs: &MinerAddresses,
start_epoch: ChainEpoch,
end_epoch: ChainEpoch,
) -> 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]);
deal_ids[0]
}

pub fn generate_and_publish_deal_for_piece(
rt: &mut MockRuntime,
client: Address,
Expand Down Expand Up @@ -887,3 +902,24 @@ 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<SectorDeals>,
) -> VerifyDealsForActivationReturn {
let param = VerifyDealsForActivationParams { sectors: sector_deals };
rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]);
rt.set_caller(*MINER_ACTOR_CODE_ID, provider);

let ret: VerifyDealsForActivationReturn = rt
.call::<MarketActor>(
Method::VerifyDealsForActivation as u64,
&RawBytes::serialize(param).unwrap(),
)
.unwrap()
.deserialize()
.expect("VerifyDealsForActivation failed!");
rt.verify();
ret
}
266 changes: 266 additions & 0 deletions actors/market/tests/verify_deals_for_activation_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
// Copyright 2019-2022 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

mod harness;
use fil_actor_market::policy::detail::deal_weight;
use fil_actor_market::{Actor as MarketActor, Method, SectorDeals, VerifyDealsForActivationParams};
use fil_actors_runtime::test_utils::{
expect_abort, expect_abort_contains_message, ACCOUNT_ACTOR_CODE_ID, MINER_ACTOR_CODE_ID,
};
use fil_actors_runtime::EPOCHS_IN_DAY;
use fvm_ipld_encoding::RawBytes;
use fvm_shared::address::Address;
use fvm_shared::bigint::BigInt;
use fvm_shared::clock::ChainEpoch;
use fvm_shared::error::ExitCode;
use harness::*;
use num_traits::Zero;

const START_EPOCH: ChainEpoch = 10;
const END_EPOCH: ChainEpoch = 200 * EPOCHS_IN_DAY;
const SECTOR_EXPIRY: ChainEpoch = END_EPOCH + 200;
const MINER_ADDRESSES: MinerAddresses = MinerAddresses {
owner: OWNER_ADDR,
worker: WORKER_ADDR,
provider: PROVIDER_ADDR,
control: vec![],
};

#[test]
fn verify_deal_and_get_deal_weight_for_unverified_deal_proposal() {
let mut rt = setup();
let deal_id =
generate_and_publish_deal(&mut rt, CLIENT_ADDR, &MINER_ADDRESSES, START_EPOCH, END_EPOCH);
let deal_proposal = get_deal_proposal(&mut rt, deal_id);

let response = verify_deals_for_activation(
&mut rt,
PROVIDER_ADDR,
vec![SectorDeals { sector_expiry: SECTOR_EXPIRY, deal_ids: vec![deal_id] }],
);

assert_eq!(1, response.sectors.len());
assert_eq!(BigInt::zero(), response.sectors[0].verified_deal_weight);
assert_eq!(deal_weight(&deal_proposal), response.sectors[0].deal_weight);

check_state(&rt);
}

#[test]
fn verify_deal_and_get_deal_weight_for_verified_deal_proposal() {
let mut rt = setup();
let deal_id = generate_and_publish_verified_deal(
&mut rt,
CLIENT_ADDR,
&MINER_ADDRESSES,
START_EPOCH,
END_EPOCH,
);
let deal_proposal = get_deal_proposal(&mut rt, deal_id);

let response = verify_deals_for_activation(
&mut rt,
PROVIDER_ADDR,
vec![SectorDeals { sector_expiry: SECTOR_EXPIRY, deal_ids: vec![deal_id] }],
);

assert_eq!(1, response.sectors.len());
assert_eq!(deal_weight(&deal_proposal), response.sectors[0].verified_deal_weight);
assert_eq!(BigInt::zero(), response.sectors[0].deal_weight);

check_state(&rt);
}

#[test]
fn verification_and_weights_for_verified_and_unverified_deals() {
let mut rt = setup();
let mut create_deal = |end_epoch, verified| {
let mut deal = generate_deal_and_add_funds(
&mut rt,
CLIENT_ADDR,
&MINER_ADDRESSES,
START_EPOCH,
end_epoch,
);
deal.verified_deal = verified;
deal
};

let verified_deal_1 = create_deal(END_EPOCH, true);
let verified_deal_2 = create_deal(END_EPOCH + 1, true);
let unverified_deal_1 = create_deal(END_EPOCH + 2, false);
let unverified_deal_2 = create_deal(END_EPOCH + 3, false);

rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR);
let deal_ids = publish_deals(
&mut rt,
&MINER_ADDRESSES,
&[
verified_deal_1.clone(),
verified_deal_2.clone(),
unverified_deal_1.clone(),
unverified_deal_2.clone(),
],
);

let response = verify_deals_for_activation(
&mut rt,
PROVIDER_ADDR,
vec![SectorDeals { sector_expiry: SECTOR_EXPIRY, deal_ids }],
);

let verified_weight = deal_weight(&verified_deal_1) + deal_weight(&verified_deal_2);
let unverified_weight = deal_weight(&unverified_deal_1) + deal_weight(&unverified_deal_2);

assert_eq!(1, response.sectors.len());
assert_eq!(verified_weight, response.sectors[0].verified_deal_weight);
assert_eq!(unverified_weight, response.sectors[0].deal_weight);

check_state(&rt);
}

#[test]
fn fail_when_caller_is_not_a_storage_miner_actor() {
let mut rt = setup();
let deal_id =
generate_and_publish_deal(&mut rt, CLIENT_ADDR, &MINER_ADDRESSES, START_EPOCH, END_EPOCH);

rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR);
rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]);

let params = VerifyDealsForActivationParams {
sectors: vec![SectorDeals { sector_expiry: SECTOR_EXPIRY, deal_ids: vec![deal_id] }],
};
expect_abort(
ExitCode::USR_FORBIDDEN,
rt.call::<MarketActor>(
Method::VerifyDealsForActivation as u64,
&RawBytes::serialize(params).unwrap(),
),
);

rt.verify();
check_state(&rt);
}

#[test]
fn fail_when_deal_proposal_is_not_found() {
let mut rt = setup();

let params = VerifyDealsForActivationParams {
sectors: vec![SectorDeals { sector_expiry: SECTOR_EXPIRY, deal_ids: vec![1] }],
};
rt.set_caller(*MINER_ACTOR_CODE_ID, PROVIDER_ADDR);
rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]);
expect_abort(
ExitCode::USR_NOT_FOUND,
rt.call::<MarketActor>(
Method::VerifyDealsForActivation as u64,
&RawBytes::serialize(params).unwrap(),
),
);

rt.verify();
check_state(&rt);
}

#[test]
fn fail_when_caller_is_not_the_provider() {
let mut rt = setup();
let deal_id =
generate_and_publish_deal(&mut rt, CLIENT_ADDR, &MINER_ADDRESSES, START_EPOCH, END_EPOCH);

rt.set_caller(*MINER_ACTOR_CODE_ID, Address::new_id(205));
rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]);

let params = VerifyDealsForActivationParams {
sectors: vec![SectorDeals { sector_expiry: SECTOR_EXPIRY, deal_ids: vec![deal_id] }],
};
expect_abort(
ExitCode::USR_FORBIDDEN,
rt.call::<MarketActor>(
Method::VerifyDealsForActivation as u64,
&RawBytes::serialize(params).unwrap(),
),
);

rt.verify();
check_state(&rt);
}

#[test]
fn fail_when_current_epoch_is_greater_than_proposal_start_epoch() {
let mut rt = setup();
let deal_id =
generate_and_publish_deal(&mut rt, CLIENT_ADDR, &MINER_ADDRESSES, START_EPOCH, END_EPOCH);
rt.set_epoch(START_EPOCH + 1);

rt.set_caller(*MINER_ACTOR_CODE_ID, PROVIDER_ADDR);
rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]);

let params = VerifyDealsForActivationParams {
sectors: vec![SectorDeals { sector_expiry: SECTOR_EXPIRY, deal_ids: vec![deal_id] }],
};
expect_abort(
ExitCode::USR_ILLEGAL_ARGUMENT,
rt.call::<MarketActor>(
Method::VerifyDealsForActivation as u64,
&RawBytes::serialize(params).unwrap(),
),
);

rt.verify();
check_state(&rt);
}

#[test]
fn fail_when_deal_end_epoch_is_greater_than_sector_expiration() {
let mut rt = setup();
let deal_id =
generate_and_publish_deal(&mut rt, CLIENT_ADDR, &MINER_ADDRESSES, START_EPOCH, END_EPOCH);

rt.set_caller(*MINER_ACTOR_CODE_ID, PROVIDER_ADDR);
rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]);

let params = VerifyDealsForActivationParams {
sectors: vec![SectorDeals { sector_expiry: END_EPOCH - 1, deal_ids: vec![deal_id] }],
};
expect_abort(
ExitCode::USR_ILLEGAL_ARGUMENT,
rt.call::<MarketActor>(
Method::VerifyDealsForActivation as u64,
&RawBytes::serialize(params).unwrap(),
),
);

rt.verify();
check_state(&rt);
}

#[test]
fn fail_when_the_same_deal_id_is_passed_multiple_times() {
let mut rt = setup();
let deal_id =
generate_and_publish_deal(&mut rt, CLIENT_ADDR, &MINER_ADDRESSES, START_EPOCH, END_EPOCH);

rt.set_caller(*MINER_ACTOR_CODE_ID, PROVIDER_ADDR);
rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]);

let params = VerifyDealsForActivationParams {
sectors: vec![SectorDeals {
sector_expiry: SECTOR_EXPIRY,
deal_ids: vec![deal_id, deal_id],
}],
};
expect_abort_contains_message(
ExitCode::USR_ILLEGAL_ARGUMENT,
"multiple times",
rt.call::<MarketActor>(
Method::VerifyDealsForActivation as u64,
&RawBytes::serialize(params).unwrap(),
),
);

rt.verify();
check_state(&rt);
}