diff --git a/actors/market/tests/harness.rs b/actors/market/tests/harness.rs index 4a8158793..6b116e0c4 100644 --- a/actors/market/tests/harness.rs +++ b/actors/market/tests/harness.rs @@ -666,7 +666,6 @@ pub fn assert_deal_failure( } else { generate_deal_proposal(CLIENT_ADDR, PROVIDER_ADDR, start_epoch, end_epoch) }; - deal_proposal.verified_deal = false; rt.set_epoch(current_epoch); post_setup(&mut rt, &mut deal_proposal); @@ -752,7 +751,7 @@ pub fn generate_and_publish_deal_for_piece( let deal = DealProposal { piece_cid, piece_size, - verified_deal: true, + verified_deal: false, client, provider: addrs.provider, label: "label".to_string(), @@ -822,7 +821,7 @@ fn generate_deal_proposal_with_collateral( DealProposal { piece_cid, piece_size, - verified_deal: true, + verified_deal: false, client, provider, label: "label".to_string(), diff --git a/actors/market/tests/publish_storage_deals_failures.rs b/actors/market/tests/publish_storage_deals_failures.rs index b519b5198..89e50fd7b 100644 --- a/actors/market/tests/publish_storage_deals_failures.rs +++ b/actors/market/tests/publish_storage_deals_failures.rs @@ -2,13 +2,18 @@ // SPDX-License-Identifier: Apache-2.0, MIT use fil_actor_market::policy::deal_provider_collateral_bounds; -use fil_actor_market::DealProposal; +use fil_actor_market::{ + Actor as MarketActor, ClientDealProposal, DealProposal, Method, PublishStorageDealsParams, + PublishStorageDealsReturn, +}; use fil_actors_runtime::network::EPOCHS_IN_DAY; use fil_actors_runtime::runtime::Policy; use fil_actors_runtime::test_utils::*; +use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; use fvm_shared::bigint::BigInt; use fvm_shared::clock::ChainEpoch; +use fvm_shared::crypto::signature::Signature; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; use fvm_shared::piece::PaddedPieceSize; @@ -203,4 +208,250 @@ mod publish_storage_deals_failures { }; assert_deal_failure(true, f, ExitCode::USR_ILLEGAL_ARGUMENT, Ok(())); } + + #[test] + fn fail_when_client_has_some_funds_but_not_enough_for_a_deal() { + let mut rt = setup(); + + let amount = TokenAmount::from(100u8); + add_participant_funds(&mut rt, CLIENT_ADDR, amount.clone()); + let start_epoch = 42; + let end_epoch = start_epoch + 200 * EPOCHS_IN_DAY; + let deal1 = generate_deal_proposal(CLIENT_ADDR, PROVIDER_ADDR, start_epoch, end_epoch); + assert!(amount < deal1.client_balance_requirement()); + add_provider_funds(&mut rt, deal1.clone().provider_collateral, &MinerAddresses::default()); + publish_deals_expect_abort( + &mut rt, + &MinerAddresses::default(), + deal1, + ExitCode::USR_ILLEGAL_ARGUMENT, + ); + + check_state(&rt); + } + + #[test] + fn fail_when_provider_has_some_funds_but_not_enough_for_a_deal() { + let start_epoch = 10; + let end_epoch = start_epoch + 200 * EPOCHS_IN_DAY; + + let mut rt = setup(); + + let amount = TokenAmount::from(1u8); + add_provider_funds(&mut rt, amount.clone(), &MinerAddresses::default()); + let deal1 = generate_deal_proposal(CLIENT_ADDR, PROVIDER_ADDR, start_epoch, end_epoch); + assert!(amount < deal1.client_balance_requirement()); + add_participant_funds(&mut rt, CLIENT_ADDR, deal1.client_balance_requirement()); + + let buf = RawBytes::serialize(deal1.clone()).expect("failed to marshal deal proposal"); + let sig = Signature::new_bls("does not matter".as_bytes().to_vec()); + let params = PublishStorageDealsParams { + deals: vec![ClientDealProposal { + proposal: deal1.clone(), + client_signature: sig.clone(), + }], + }; + + rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + 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); + rt.expect_verify_signature(ExpectedVerifySig { + sig, + signer: deal1.client, + plaintext: buf.to_vec(), + result: Ok(()), + }); + expect_abort( + ExitCode::USR_ILLEGAL_ARGUMENT, + rt.call::( + Method::PublishStorageDeals as u64, + &RawBytes::serialize(params).unwrap(), + ), + ); + + rt.verify(); + check_state(&rt); + } + + #[test] + fn fail_when_deals_have_different_providers() { + let start_epoch = 10; + let end_epoch = start_epoch + 200 * EPOCHS_IN_DAY; + + let mut rt = setup(); + let deal1 = generate_deal_and_add_funds( + &mut rt, + CLIENT_ADDR, + &MinerAddresses::default(), + start_epoch, + end_epoch, + ); + let m2 = MinerAddresses { provider: Address::new_id(1000), ..MinerAddresses::default() }; + + let deal2 = generate_deal_and_add_funds(&mut rt, CLIENT_ADDR, &m2, 1, end_epoch); + + let buf1 = RawBytes::serialize(deal1.clone()).expect("failed to marshal deal proposal"); + let buf2 = RawBytes::serialize(deal2.clone()).expect("failed to marshal deal proposal"); + let sig = Signature::new_bls("does not matter".as_bytes().to_vec()); + let params = PublishStorageDealsParams { + deals: vec![ + ClientDealProposal { proposal: deal1.clone(), client_signature: sig.clone() }, + ClientDealProposal { proposal: deal2.clone(), client_signature: sig.clone() }, + ], + }; + + rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + 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); + rt.expect_verify_signature(ExpectedVerifySig { + sig: sig.clone(), + signer: deal1.client, + plaintext: buf1.to_vec(), + result: Ok(()), + }); + rt.expect_verify_signature(ExpectedVerifySig { + sig, + signer: deal2.client, + plaintext: buf2.to_vec(), + result: Ok(()), + }); + + let psd_ret: PublishStorageDealsReturn = rt + .call::( + Method::PublishStorageDeals as u64, + &RawBytes::serialize(params).unwrap(), + ) + .unwrap() + .deserialize() + .unwrap(); + + let valid: Vec = psd_ret.valid_deals.bounded_iter(std::u64::MAX).unwrap().collect(); + assert_eq!(vec![0], valid); + + rt.verify(); + check_state(&rt); + } + + #[test] + fn fail_when_caller_is_not_of_signable_type() { + let start_epoch = 10; + let end_epoch = start_epoch + 200 * EPOCHS_IN_DAY; + + let mut rt = setup(); + let deal = generate_deal_proposal(CLIENT_ADDR, PROVIDER_ADDR, start_epoch, end_epoch); + let sig = Signature::new_bls("does not matter".as_bytes().to_vec()); + let params = PublishStorageDealsParams { + deals: vec![ClientDealProposal { proposal: deal, client_signature: sig }], + }; + let w = Address::new_id(1000); + rt.set_caller(*MINER_ACTOR_CODE_ID, w); + rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + expect_abort( + ExitCode::USR_FORBIDDEN, + rt.call::( + Method::PublishStorageDeals as u64, + &RawBytes::serialize(params).unwrap(), + ), + ); + check_state(&rt); + } + + #[test] + fn fail_when_no_deals_in_params() { + let mut rt = setup(); + let params = PublishStorageDealsParams { deals: vec![] }; + rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); + rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + expect_abort( + ExitCode::USR_ILLEGAL_ARGUMENT, + rt.call::( + Method::PublishStorageDeals as u64, + &RawBytes::serialize(params).unwrap(), + ), + ); + check_state(&rt); + } + + #[test] + fn fail_to_resolve_provider_address() { + let start_epoch = 10; + let end_epoch = start_epoch + 200 * EPOCHS_IN_DAY; + + let mut rt = setup(); + let mut deal = generate_deal_proposal(CLIENT_ADDR, PROVIDER_ADDR, start_epoch, end_epoch); + deal.provider = new_bls_addr(100); + + let sig = Signature::new_bls("does not matter".as_bytes().to_vec()); + let params = PublishStorageDealsParams { + deals: vec![ClientDealProposal { proposal: deal, client_signature: sig }], + }; + rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); + rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + expect_abort( + ExitCode::USR_NOT_FOUND, + rt.call::( + Method::PublishStorageDeals as u64, + &RawBytes::serialize(params).unwrap(), + ), + ); + check_state(&rt); + } + + #[test] + fn caller_is_not_the_same_as_the_worker_address_for_miner() { + let start_epoch = 10; + let end_epoch = start_epoch + 200 * EPOCHS_IN_DAY; + + let mut rt = setup(); + let deal = generate_deal_proposal(CLIENT_ADDR, PROVIDER_ADDR, start_epoch, end_epoch); + let sig = Signature::new_bls("does not matter".as_bytes().to_vec()); + let params = PublishStorageDealsParams { + deals: vec![ClientDealProposal { proposal: deal, client_signature: sig }], + }; + + rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + expect_provider_control_address(&mut rt, PROVIDER_ADDR, OWNER_ADDR, WORKER_ADDR); + rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, Address::new_id(999)); + expect_abort( + ExitCode::USR_FORBIDDEN, + rt.call::( + Method::PublishStorageDeals as u64, + &RawBytes::serialize(params).unwrap(), + ), + ); + + rt.verify(); + check_state(&rt); + } + + #[test] + fn fails_if_provider_is_not_a_storage_miner_actor() { + let mut rt = setup(); + + // deal provider will be a Storage Miner Actor. + let p2 = Address::new_id(505); + rt.set_address_actor_type(p2, *POWER_ACTOR_CODE_ID); + let deal = + generate_deal_proposal(CLIENT_ADDR, p2, ChainEpoch::from(1), ChainEpoch::from(5)); + + let sig = Signature::new_bls("does not matter".as_bytes().to_vec()); + let params = PublishStorageDealsParams { + deals: vec![ClientDealProposal { proposal: deal, client_signature: sig }], + }; + + rt.expect_validate_caller_type(vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]); + rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR); + expect_abort( + ExitCode::USR_ILLEGAL_ARGUMENT, + rt.call::( + Method::PublishStorageDeals as u64, + &RawBytes::serialize(params).unwrap(), + ), + ); + + rt.verify(); + check_state(&rt); + } }