Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Market Actor v3 #1010

Merged
merged 16 commits into from
Feb 12, 2021
7 changes: 4 additions & 3 deletions vm/actor/src/builtin/init/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ mod types;
pub use self::state::State;
pub use self::types::*;
use crate::{
make_map, ActorDowncast, MINER_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID, PAYCH_ACTOR_CODE_ID,
POWER_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR,
make_empty_map, ActorDowncast, MINER_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID,
PAYCH_ACTOR_CODE_ID, POWER_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR,
};
use address::Address;
use cid::Cid;
use fil_types::HAMT_BIT_WIDTH;
use ipld_blockstore::BlockStore;
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
Expand Down Expand Up @@ -39,7 +40,7 @@ impl Actor {
{
let sys_ref: &Address = &SYSTEM_ACTOR_ADDR;
rt.validate_immediate_caller_is(std::iter::once(sys_ref))?;
let mut empty_map = make_map::<_, ()>(rt.store());
let mut empty_map = make_empty_map::<_, ()>(rt.store(), HAMT_BIT_WIDTH);
let root = empty_map.flush().map_err(|err| {
err.downcast_default(ExitCode::ErrIllegalState, "failed to construct state")
})?;
Expand Down
346 changes: 230 additions & 116 deletions vm/actor/src/builtin/market/mod.rs

Large diffs are not rendered by default.

152 changes: 112 additions & 40 deletions vm/actor/src/builtin/market/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@
// SPDX-License-Identifier: Apache-2.0, MIT

use super::{policy::*, types::*, DealProposal, DealState, DEAL_UPDATES_INTERVAL};
use crate::{make_map_with_root, ActorDowncast, BalanceTable, DealID, Map, SetMultimap};
use crate::{make_empty_map, ActorDowncast, BalanceTable, DealID, Set, SetMultimap};
use address::Address;
use cid::Cid;
use clock::{ChainEpoch, EPOCH_UNDEFINED};
use encoding::tuple::*;
use encoding::Cbor;
use fil_types::HAMT_BIT_WIDTH;
use ipld_amt::Amt;
use ipld_blockstore::BlockStore;
use num_bigint::{bigint_ser, Sign};
use num_traits::Zero;
use num_bigint::bigint_ser;
use num_traits::{Signed, Zero};
use std::error::Error as StdError;
use vm::{actor_error, ActorError, ExitCode, TokenAmount};

Expand Down Expand Up @@ -54,21 +56,39 @@ pub struct State {
}

impl State {
pub fn new(empty_arr: Cid, empty_map: Cid, empty_mset: Cid) -> Self {
Self {
proposals: empty_arr,
states: empty_arr,
pending_proposals: empty_map,
escrow_table: empty_map,
locked_table: empty_map,
pub fn new<BS: BlockStore>(store: &BS) -> Result<Self, Box<dyn StdError>> {
let empty_proposals_array =
Amt::<(), BS>::new_with_bit_width(store, PROPOSALS_AMT_BITWIDTH)
.flush()
.map_err(|e| format!("Failed to create empty proposals array: {}", e))?;
let empty_states_array = Amt::<(), BS>::new_with_bit_width(store, STATES_AMT_BITWIDTH)
.flush()
.map_err(|e| format!("Failed to create empty states array: {}", e))?;

let empty_pending_proposals_map = make_empty_map::<_, ()>(store, HAMT_BIT_WIDTH)
.flush()
.map_err(|e| format!("Failed to create empty pending proposals map state: {}", e))?;
let empty_balance_table = BalanceTable::new(store)
.root()
.map_err(|e| format!("Failed to create empty balance table map: {}", e))?;

let empty_deal_ops_hamt = SetMultimap::new(store)
.root()
.map_err(|e| format!("Failed to create empty multiset: {}", e))?;
Ok(Self {
proposals: empty_proposals_array,
states: empty_states_array,
pending_proposals: empty_pending_proposals_map,
escrow_table: empty_balance_table,
locked_table: empty_balance_table,
next_id: 0,
deal_ops_by_epoch: empty_mset,
deal_ops_by_epoch: empty_deal_ops_hamt,
last_cron: EPOCH_UNDEFINED,

total_client_locked_colateral: TokenAmount::default(),
total_provider_locked_colateral: TokenAmount::default(),
total_client_storage_fee: TokenAmount::default(),
}
})
}

ec2 marked this conversation as resolved.
Show resolved Hide resolved
pub fn total_locked(&self) -> TokenAmount {
Expand All @@ -85,19 +105,32 @@ impl State {
}
}

fn deal_get_payment_remaining(deal: &DealProposal, mut slash_epoch: ChainEpoch) -> TokenAmount {
assert!(
slash_epoch <= deal.end_epoch,
"Current epoch must be before the end epoch of the deal"
);
fn deal_get_payment_remaining(
deal: &DealProposal,
mut slash_epoch: ChainEpoch,
) -> Result<TokenAmount, ActorError> {
if slash_epoch > deal.end_epoch {
return Err(actor_error!(
ErrIllegalState,
"deal slash epoch {} after end epoch {}",
slash_epoch,
deal.end_epoch
));
}

// Payments are always for start -> end epoch irrespective of when the deal is slashed.
slash_epoch = std::cmp::max(slash_epoch, deal.start_epoch);

let duration_remaining = deal.end_epoch - slash_epoch;
assert!(duration_remaining >= 0);
if duration_remaining < 0 {
return Err(actor_error!(
ErrIllegalState,
"deal remaining duration negative: {}",
duration_remaining
));
}

&deal.storage_price_per_epoch * duration_remaining as u64
Ok(&deal.storage_price_per_epoch * duration_remaining as u64)
}

impl Cbor for State {}
Expand Down Expand Up @@ -129,7 +162,7 @@ pub(super) struct MarketStateMutation<'bs, 's, BS> {
pub(super) escrow_table: Option<BalanceTable<'bs, BS>>,

pub(super) pending_permit: Permission,
pub(super) pending_deals: Option<Map<'bs, BS, DealProposal>>,
pub(super) pending_deals: Option<Set<'bs, BS>>,

pub(super) dpe_permit: Permission,
pub(super) deals_by_epoch: Option<SetMultimap<'bs, BS>>,
Expand Down Expand Up @@ -193,7 +226,7 @@ where
}

if self.pending_permit != Permission::Invalid {
self.pending_deals = Some(make_map_with_root(&self.st.pending_proposals, self.store)?);
self.pending_deals = Some(Set::from_root(self.store, &self.st.pending_proposals)?);
}

if self.dpe_permit != Permission::Invalid {
Expand Down Expand Up @@ -283,7 +316,7 @@ where
if self.pending_permit == Permission::Write {
if let Some(s) = &mut self.pending_deals {
self.st.pending_proposals = s
.flush()
.root()
.map_err(|e| e.downcast_wrap("failed to flush escrow table"))?;
}
}
Expand Down Expand Up @@ -315,7 +348,13 @@ where
let ever_slashed = state.slash_epoch != EPOCH_UNDEFINED;

// if the deal was ever updated, make sure it didn't happen in the future
assert!(!ever_updated || state.last_updated_epoch <= epoch);
if ever_updated && state.last_updated_epoch > epoch {
return Err(actor_error!(
ErrIllegalState,
"deal updated at future epoch {}",
state.last_updated_epoch
));
}

// This would be the case that the first callback somehow triggers before it is scheduled to
// This is expected not to be able to happen
Expand All @@ -324,14 +363,21 @@ where
}

let payment_end_epoch = if ever_slashed {
assert!(
state.slash_epoch <= deal.end_epoch,
"Epoch slashed must be less or equal to the end epoch"
);
assert!(
epoch >= state.slash_epoch,
"Epoch slashed must be less or equal to the end epoch"
);
if epoch < state.slash_epoch {
return Err(actor_error!(
ErrIllegalState,
"current epoch less than deal slash epoch {}",
state.slash_epoch
));
}
if state.slash_epoch > deal.end_epoch {
return Err(actor_error!(
ErrIllegalState,
"deal slash epoch {} after deal end {}",
state.slash_epoch,
deal.end_epoch
));
}
state.slash_epoch
} else {
std::cmp::min(deal.end_epoch, epoch)
Expand All @@ -352,7 +398,7 @@ where

if ever_slashed {
// unlock client collateral and locked storage fee
let payment_remaining = deal_get_payment_remaining(&deal, state.slash_epoch);
let payment_remaining = deal_get_payment_remaining(&deal, state.slash_epoch)?;

// Unlock remaining storage fee
self.unlock_balance(&deal.client, &payment_remaining, Reason::ClientStorageFee)
Expand Down Expand Up @@ -461,10 +507,12 @@ where
where
BS: BlockStore,
{
assert_ne!(
state.sector_start_epoch, EPOCH_UNDEFINED,
"Sector start epoch must be initialized at this point"
);
if state.sector_start_epoch == EPOCH_UNDEFINED {
return Err(actor_error!(
ErrIllegalState,
"start sector epoch undefined"
));
}

self.unlock_balance(
&deal.provider,
Expand Down Expand Up @@ -504,7 +552,13 @@ where
addr: &Address,
amount: &TokenAmount,
) -> Result<(), ActorError> {
assert_ne!(amount.sign(), Sign::Minus);
if amount.is_negative() {
return Err(actor_error!(
ErrIllegalState,
"cannot lock negative amount {}",
amount
));
}

let prev_locked = self.locked_table.as_ref().unwrap().get(addr).map_err(|e| {
e.downcast_default(ExitCode::ErrIllegalState, "failed to get locked balance")
Expand Down Expand Up @@ -559,7 +613,13 @@ where
amount: &TokenAmount,
lock_reason: Reason,
) -> Result<(), Box<dyn StdError>> {
assert_ne!(amount.sign(), Sign::Minus);
if amount.is_negative() {
return Err(Box::new(actor_error!(
ErrIllegalState,
"unlock negative amount: {}",
amount
)));
}
self.locked_table
.as_mut()
.unwrap()
Expand Down Expand Up @@ -587,7 +647,13 @@ where
to_addr: &Address,
amount: &TokenAmount,
) -> Result<(), ActorError> {
assert!(amount >= &TokenAmount::from(0));
if amount.is_negative() {
return Err(actor_error!(
ErrIllegalState,
"transfer negative amount: {}",
amount
));
}

// Subtract from locked and escrow tables
self.escrow_table
Expand Down Expand Up @@ -615,7 +681,13 @@ where
amount: &TokenAmount,
lock_reason: Reason,
) -> Result<(), Box<dyn StdError>> {
assert!(amount >= &TokenAmount::from(0));
if amount.is_negative() {
return Err(Box::new(actor_error!(
ErrIllegalState,
"negative amount to slash: {}",
amount
)));
}

// Subtract from locked and escrow tables
self.escrow_table
Expand Down
22 changes: 18 additions & 4 deletions vm/actor/src/builtin/market/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ use ipld_amt::Amt;
use num_bigint::bigint_ser;
use vm::{DealID, TokenAmount};

pub const PROPOSALS_AMT_BITWIDTH: usize = 5;
pub const STATES_AMT_BITWIDTH: usize = 6;

#[derive(Serialize_tuple, Deserialize_tuple)]
pub struct WithdrawBalanceParams {
pub provider_or_client: Address,
Expand Down Expand Up @@ -40,11 +43,18 @@ pub struct PublishStorageDealsReturn {
pub ids: Vec<DealID>,
}

// Changed since V2:
// - Array of Sectors rather than just one
// - Removed SectorStart
#[derive(Serialize_tuple, Deserialize_tuple)]
pub struct VerifyDealsForActivationParams {
pub deal_ids: Vec<DealID>,
pub sectors: Vec<SectorDeals>,
}

#[derive(Serialize_tuple, Deserialize_tuple)]
pub struct SectorDeals {
pub sector_expiry: ChainEpoch,
pub sector_start: ChainEpoch,
pub deal_ids: Vec<DealID>,
}

#[derive(Serialize_tuple)]
Expand All @@ -56,12 +66,16 @@ pub struct VerifyDealsForActivationParamsRef<'a> {

#[derive(Serialize_tuple, Deserialize_tuple, Default)]
pub struct VerifyDealsForActivationReturn {
pub sectors: Vec<SectorWeights>,
}

#[derive(Serialize_tuple, Deserialize_tuple, Default)]
pub struct SectorWeights {
pub deal_space: u64,
#[serde(with = "bigint_ser")]
pub deal_weight: DealWeight,
#[serde(with = "bigint_ser")]
pub verified_deal_weight: DealWeight,
// * Added in v2
pub deal_space: u64,
}

#[derive(Serialize_tuple, Deserialize_tuple)]
Expand Down
Loading