Skip to content

Commit

Permalink
Market Actor v3 (#1010)
Browse files Browse the repository at this point in the history
* update activate_deals, verify_deals_for_activation

* on_miner_sectors_terminate

* finish market actor

* fmt

* compiles

* fix tests

* lint

* fix suggestions

* remove asserts

* removed another assert

* force hamt bitwidth in Map

* lint

* remove make_map, add new make_empty_map for bitwidth

* fmt
  • Loading branch information
ec2 authored Feb 12, 2021
1 parent 4d0c864 commit 0289e34
Show file tree
Hide file tree
Showing 16 changed files with 489 additions and 230 deletions.
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(),
}
})
}

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

0 comments on commit 0289e34

Please sign in to comment.