diff --git a/vm/actor/src/builtin/codes.rs b/vm/actor/src/builtin/codes.rs index 6e3c6b259498..b3d463b05282 100644 --- a/vm/actor/src/builtin/codes.rs +++ b/vm/actor/src/builtin/codes.rs @@ -14,6 +14,7 @@ lazy_static! { pub static ref PAYCH_ACTOR_CODE_ID: Cid = make_builtin(b"fil/1/paymentchannel"); pub static ref MULTISIG_ACTOR_CODE_ID: Cid = make_builtin(b"fil/1/multisig"); pub static ref REWARD_ACTOR_CODE_ID: Cid = make_builtin(b"fil/1/reward"); + pub static ref VERIFIED_ACTOR_CODE_ID: Cid = make_builtin(b"fil/1/verifiedregistry"); // Set of actor code types that can represent external signing parties. pub static ref CALLER_TYPES_SIGNABLE: [Cid; 2] = diff --git a/vm/actor/src/builtin/market/deal.rs b/vm/actor/src/builtin/market/deal.rs index 0ebf1b8cb085..5260f9edd75e 100644 --- a/vm/actor/src/builtin/market/deal.rs +++ b/vm/actor/src/builtin/market/deal.rs @@ -7,6 +7,7 @@ use cid::Cid; use clock::ChainEpoch; use crypto::Signature; use encoding::tuple::*; +use encoding::Cbor; use fil_types::PaddedPieceSize; use num_bigint::biguint_ser; use vm::TokenAmount; @@ -42,6 +43,8 @@ pub struct DealProposal { pub client_collateral: TokenAmount, } +impl Cbor for DealProposal {} + impl DealProposal { pub fn duration(&self) -> ChainEpoch { self.end_epoch - self.start_epoch diff --git a/vm/actor/src/builtin/market/mod.rs b/vm/actor/src/builtin/market/mod.rs index c303bea0541f..0beb9a1e5fb9 100644 --- a/vm/actor/src/builtin/market/mod.rs +++ b/vm/actor/src/builtin/market/mod.rs @@ -11,8 +11,11 @@ use self::policy::*; pub use self::state::State; pub use self::types::*; use crate::{ - make_map, request_miner_control_addrs, BalanceTable, DealID, OptionalEpoch, SetMultimap, - BURNT_FUNDS_ACTOR_ADDR, CALLER_TYPES_SIGNABLE, MINER_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR, + make_map, request_miner_control_addrs, + verifreg::{BytesParams, Method as VerifregMethod}, + BalanceTable, DealID, OptionalEpoch, SetMultimap, BURNT_FUNDS_ACTOR_ADDR, + CALLER_TYPES_SIGNABLE, CRON_ACTOR_ADDR, MINER_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR, + VERIFIED_REGISTRY_ACTOR_ADDR, }; use address::Address; use cid::Cid; @@ -37,11 +40,11 @@ pub enum Method { Constructor = METHOD_CONSTRUCTOR, AddBalance = 2, WithdrawBalance = 3, - HandleExpiredDeals = 4, - PublishStorageDeals = 5, - VerifyDealsOnSectorProveCommit = 6, - OnMinerSectorsTerminate = 7, - ComputeDataCommitment = 8, + PublishStorageDeals = 4, + VerifyDealsOnSectorProveCommit = 5, + OnMinerSectorsTerminate = 6, + ComputeDataCommitment = 7, + CronTick = 8, } /// Market Actor pub struct Actor; @@ -123,12 +126,12 @@ impl Actor { { let (nominal, recipient) = escrow_address(rt, ¶ms.provider_or_client)?; - let mut amount_slashed_total = TokenAmount::zero(); + let amount_slashed_total = TokenAmount::zero(); let amount_extracted = rt.transaction::<_, Result, _>(|st: &mut State, rt| { - // Before any operations that check the balance tables for funds, execute all deferred - // deal state updates. - amount_slashed_total += st.update_pending_deal_states_for_party(rt, &nominal)?; + // The withdrawable amount might be slightly less than nominal + // depending on whether or not all relevant entries have been processed + // by cron let min_balance = st.get_locked_balance(rt.store(), &nominal)?; @@ -150,6 +153,7 @@ impl Actor { Ok(ex) })??; + // TODO this will never be hit if amount_slashed_total > BigUint::zero() { rt.send( &*BURNT_FUNDS_ACTOR_ADDR, @@ -167,31 +171,6 @@ impl Actor { Ok(()) } - fn handle_expired_deals( - rt: &mut RT, - params: HandleExpiredDealsParams, - ) -> Result<(), ActorError> - where - BS: BlockStore, - RT: Runtime, - { - rt.validate_immediate_caller_type(CALLER_TYPES_SIGNABLE.iter())?; - - let slashed = rt.transaction(|st: &mut State, rt| { - st.update_pending_deal_states(rt.store(), params.deal_ids, rt.curr_epoch()) - })??; - - // TODO: award some small portion of slashed to caller as incentive - - rt.send( - &*BURNT_FUNDS_ACTOR_ADDR, - METHOD_SEND, - &Serialized::default(), - &slashed, - )?; - Ok(()) - } - /// Publish a new set of storage deals (not yet included in a sector). fn publish_storage_deals( rt: &mut RT, @@ -201,8 +180,6 @@ impl Actor { BS: BlockStore, RT: Runtime, { - let mut amount_slashed_total = TokenAmount::zero(); - // Deal message must have a From field identical to the provider of all the deals. // This allows us to retain and verify only the client's signature in each deal proposal itself. rt.validate_immediate_caller_type(CALLER_TYPES_SIGNABLE.iter())?; @@ -225,6 +202,24 @@ impl Actor { )); } + for deal in ¶ms.deals { + // Check VerifiedClient allowed cap and deduct PieceSize from cap. + // Either the DealSize is within the available DataCap of the VerifiedClient + // or this message will fail. We do not allow a deal that is partially verified. + if deal.proposal.verified_deal { + let ser_params = Serialized::serialize(&BytesParams { + address: deal.proposal.client, + deal_size: BigUint::from(deal.proposal.piece_size.0), + })?; + rt.send( + &*VERIFIED_REGISTRY_ACTOR_ADDR, + VerifregMethod::UseBytes as u64, + &ser_params, + &TokenAmount::zero(), + )?; + } + } + // All deals should have the same provider so get worker once let provider_raw = params.deals[0].proposal.provider; let provider = rt.resolve_address(&provider_raw)?; @@ -233,7 +228,7 @@ impl Actor { rt.transaction(|st: &mut State, rt| { let mut prop = Amt::load(&st.proposals, rt.store()) .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; - let mut dbp = SetMultimap::from_root(rt.store(), &st.deal_ids_by_party) + let mut deal_ops = SetMultimap::from_root(rt.store(), &st.deal_ops_by_epoch) .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; for mut deal in params.deals { @@ -252,14 +247,6 @@ impl Actor { deal.proposal.provider = provider; deal.proposal.client = client; - // Before any operations that check the balance tables for funds, execute all deferred - // deal state updates. - // - // Note: as an optimization, implementations may cache efficient data structures indicating - // which of the following set of updates are redundant and can be skipped. - amount_slashed_total += st.update_pending_deal_states_for_party(rt, &client)?; - amount_slashed_total += st.update_pending_deal_states_for_party(rt, &provider)?; - st.lock_balance_or_abort( rt.store(), &client, @@ -273,31 +260,25 @@ impl Actor { let id = st.generate_storage_deal_id(); + deal_ops + .put(deal.proposal.start_epoch, id) + .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e))?; + prop.set(id, deal.proposal) .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; - dbp.put(&client, id) - .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e))?; - dbp.put(&provider, id) - .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e))?; new_deal_ids.push(id); } st.proposals = prop .flush() .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; - st.deal_ids_by_party = dbp + st.deal_ops_by_epoch = deal_ops .root() .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; + Ok(()) })??; - rt.send( - &*BURNT_FUNDS_ACTOR_ADDR, - METHOD_SEND, - &Serialized::default(), - &amount_slashed_total, - )?; - Ok(PublishStorageDealsReturn { ids: new_deal_ids }) } @@ -329,15 +310,18 @@ impl Actor { .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; for id in ¶ms.deal_ids { - let mut deal: DealState = states + let deal = states .get(*id) - .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))? - .ok_or_else(|| { - ActorError::new( - ExitCode::ErrIllegalState, - "Failed to retrieve the DealState".to_owned(), - ) - })?; + .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; + + if deal.is_some() { + // Sector is currently precommitted but still not proven. + return Err(ActorError::new( + ExitCode::ErrIllegalArgument, + format!("given deal already included in another sector: {}", id), + )); + }; + let proposal: DealProposal = proposals .get(*id) .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))? @@ -352,13 +336,18 @@ impl Actor { rt.curr_epoch(), &miner_addr, params.sector_expiry, - &deal, &proposal, )?; - deal.sector_start_epoch = OptionalEpoch(Some(rt.curr_epoch())); states - .set(*id, deal) + .set( + *id, + DealState { + sector_start_epoch: OptionalEpoch(Some(rt.curr_epoch())), + last_updated_epoch: OptionalEpoch(None), + slash_epoch: OptionalEpoch(None), + }, + ) .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; // compute deal weight @@ -368,6 +357,12 @@ impl Actor { } else { total_deal_space_time += deal_space_time; } + + if proposal.verified_deal { + total_verified_deal_space_time += deal_space_time; + } else { + total_deal_space_time += deal_space_time; + } } st.states = states .flush() @@ -476,6 +471,171 @@ impl Actor { Ok(commd) } + + fn cron_tick(rt: &mut RT) -> Result<(), ActorError> + where + BS: BlockStore, + RT: Runtime, + { + rt.validate_immediate_caller_is(std::iter::once(&*CRON_ACTOR_ADDR))?; + let mut amount_slashed = BigUint::zero(); + let mut timed_out_verified_deals: Vec = Vec::new(); + + rt.transaction::, _>(|st, rt| { + let mut dbe = + SetMultimap::from_root(rt.store(), &st.deal_ops_by_epoch).map_err(|e| { + ActorError::new( + ExitCode::ErrIllegalState, + format!("failed to load deal opts set: {}", e), + ) + })?; + + let mut updates_needed: Vec<(ChainEpoch, DealID)> = Vec::new(); + + let mut states = Amt::load(&st.states, rt.store()) + .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; + + let mut et = BalanceTable::from_root(rt.store(), &st.escrow_table) + .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; + + let mut lt = BalanceTable::from_root(rt.store(), &st.locked_table) + .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; + + let mut i = st.last_cron + 1; + while i <= rt.curr_epoch() { + dbe.for_each(i, |id| { + let mut state: DealState = states + .get(id) + .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))? + .ok_or_else(|| { + ActorError::new( + ExitCode::ErrIllegalState, + format!("could not find deal state: {}", id), + ) + })?; + + let deal = st.must_get_deal(rt.store(), id)?; + // Not yet appeared in proven sector; check for timeout. + if state.sector_start_epoch.is_none() { + assert!( + rt.curr_epoch() >= deal.start_epoch, + "if sector start is not set, we must be in a timed out state" + ); + + let slashed = st.process_deal_init_timed_out( + rt.store(), + &mut et, + &mut lt, + id, + &deal, + state, + )?; + amount_slashed += slashed; + + if deal.verified_deal { + timed_out_verified_deals.push(deal.clone()); + } + } + + let (slash_amount, next_epoch) = st.update_pending_deal_state( + rt.store(), + state, + deal, + id, + &mut et, + &mut lt, + rt.curr_epoch(), + )?; + amount_slashed += slash_amount; + + if next_epoch.is_some() { + assert!(next_epoch.unwrap() > rt.curr_epoch()); + + // TODO: can we avoid having this field? + state.last_updated_epoch = OptionalEpoch(Some(rt.curr_epoch())); + + states.set(id, state).map_err(|e| { + ActorError::new( + ExitCode::ErrPlaceholder, + format!("failed to get deal: {}", e), + ) + })?; + if let OptionalEpoch(Some(idx)) = next_epoch { + updates_needed.push((idx, id)); + } + } + Ok(()) + }) + .map_err(|e| match e.downcast::() { + Ok(actor_err) => *actor_err, + Err(other) => ActorError::new( + ExitCode::ErrIllegalState, + format!("failed to iterate deals for epoch: {}", other), + ), + })?; + dbe.remove_all(i).map_err(|e| { + ActorError::new( + ExitCode::ErrIllegalState, + format!("failed to delete deals from set: {}", e), + ) + })?; + i += 1; + } + + for (epoch, deals) in updates_needed.into_iter() { + // TODO multimap should have put_many + dbe.put(epoch, deals).map_err(|e| { + ActorError::new( + ExitCode::ErrIllegalState, + format!("failed to reinsert deal IDs into epoch set: {}", e), + ) + })?; + } + + let nd_bec = dbe + .root() + .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; + + let ltc = lt + .root() + .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; + + let etc = et + .root() + .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; + + st.locked_table = ltc; + st.escrow_table = etc; + + st.deal_ops_by_epoch = nd_bec; + + st.last_cron = rt.curr_epoch(); + + Ok(()) + })??; + + for d in timed_out_verified_deals { + let ser_params = Serialized::serialize(BytesParams { + address: d.client, + deal_size: BigUint::from(d.piece_size.0), + })?; + rt.send( + &*VERIFIED_REGISTRY_ACTOR_ADDR, + VerifregMethod::RestoreBytes as u64, + &ser_params, + &TokenAmount::zero(), + )?; + } + + rt.send( + &*BURNT_FUNDS_ACTOR_ADDR, + METHOD_SEND, + &Serialized::default(), + &amount_slashed, + )?; + + Ok(()) + } } //////////////////////////////////////////////////////////////////////////////// // Checks @@ -484,7 +644,6 @@ fn validate_deal_can_activate( curr_epoch: ChainEpoch, miner_addr: &Address, sector_exp: ChainEpoch, - deal: &DealState, proposal: &DealProposal, ) -> Result<(), ActorError> { if &proposal.provider != miner_addr { @@ -494,13 +653,6 @@ fn validate_deal_can_activate( )); }; - if deal.sector_start_epoch.is_some() { - return Err(ActorError::new( - ExitCode::ErrIllegalArgument, - "Deal has already appeared in proven sector.".to_owned(), - )); - } - if curr_epoch > proposal.start_epoch { return Err(ActorError::new( ExitCode::ErrIllegalArgument, @@ -672,10 +824,6 @@ impl ActorCode for Actor { Self::withdraw_balance(rt, params.deserialize()?)?; Ok(Serialized::default()) } - Some(Method::HandleExpiredDeals) => { - Self::handle_expired_deals(rt, params.deserialize()?)?; - Ok(Serialized::default()) - } Some(Method::PublishStorageDeals) => { let res = Self::publish_storage_deals(rt, params.deserialize()?)?; Ok(Serialized::serialize(res)?) @@ -692,6 +840,10 @@ impl ActorCode for Actor { let res = Self::compute_data_commitment(rt, params.deserialize()?)?; Ok(Serialized::serialize(res)?) } + Some(Method::CronTick) => { + Self::cron_tick(rt)?; + Ok(Serialized::default()) + } _ => Err(rt.abort(ExitCode::SysErrInvalidMethod, "Invalid method")), } } diff --git a/vm/actor/src/builtin/market/policy.rs b/vm/actor/src/builtin/market/policy.rs index 80afecda945a..bf6a528f87b7 100644 --- a/vm/actor/src/builtin/market/policy.rs +++ b/vm/actor/src/builtin/market/policy.rs @@ -6,6 +6,9 @@ use fil_types::PaddedPieceSize; use num_traits::Zero; use vm::TokenAmount; +/// DealUpdatesInterval is the number of blocks between payouts for deals +pub const DEAL_UPDATED_INTERVAL: u64 = 100; + pub(super) fn deal_duration_bounds(_size: PaddedPieceSize) -> (ChainEpoch, ChainEpoch) { (0, 10000) // PARAM_FINISH } diff --git a/vm/actor/src/builtin/market/state.rs b/vm/actor/src/builtin/market/state.rs index 40167845af2d..9a63fe379d68 100644 --- a/vm/actor/src/builtin/market/state.rs +++ b/vm/actor/src/builtin/market/state.rs @@ -1,8 +1,10 @@ // Copyright 2020 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT -use super::{collateral_penalty_for_deal_activation_missed, DealProposal, DealState}; -use crate::{BalanceTable, DealID, OptionalEpoch, SetMultimap}; +use super::{ + collateral_penalty_for_deal_activation_missed, DealProposal, DealState, DEAL_UPDATED_INTERVAL, +}; +use crate::{BalanceTable, DealID, OptionalEpoch}; use address::Address; use cid::Cid; use clock::ChainEpoch; @@ -11,7 +13,6 @@ use encoding::Cbor; use ipld_amt::Amt; use ipld_blockstore::BlockStore; use num_traits::Zero; -use runtime::Runtime; use vm::{ActorError, ExitCode, TokenAmount}; /// Market actor state @@ -31,7 +32,8 @@ pub struct State { pub next_id: DealID, /// Metadata cached for efficient iteration over deals. /// SetMultimap
- pub deal_ids_by_party: Cid, + pub deal_ops_by_epoch: Cid, + pub last_cron: ChainEpoch, } impl State { @@ -42,10 +44,215 @@ impl State { escrow_table: empty_map.clone(), locked_table: empty_map, next_id: 0, - deal_ids_by_party: empty_mset, + deal_ops_by_epoch: empty_mset, + last_cron: ChainEpoch::default(), } } + //////////////////////////////////////////////////////////////////////////////// + // Deal state operations + //////////////////////////////////////////////////////////////////////////////// + #[allow(clippy::too_many_arguments)] + pub(super) fn update_pending_deal_state( + &mut self, + store: &BS, + state: DealState, + deal: DealProposal, + deal_id: DealID, + et: &mut BalanceTable, + lt: &mut BalanceTable, + epoch: ChainEpoch, + ) -> Result<(TokenAmount, OptionalEpoch), ActorError> + where + BS: BlockStore, + { + let ever_updated = state.last_updated_epoch.is_some(); + let ever_slashed = state.slash_epoch.is_some(); + + // if the deal was ever updated, make sure it didn't happen in the future + assert!(!ever_updated || state.last_updated_epoch.unwrap() <= 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 + if deal.start_epoch > epoch { + return Ok((TokenAmount::zero(), OptionalEpoch(None))); + } + + let deal_end = if ever_slashed { + assert!( + state.slash_epoch.unwrap() <= deal.end_epoch, + "Epoch slashed must be less or equal to the end epoch" + ); + state.slash_epoch.unwrap() + } else { + deal.end_epoch + }; + + let elapsed_start = if ever_updated && state.last_updated_epoch.unwrap() > deal.start_epoch + { + state.last_updated_epoch.unwrap() + } else { + deal.start_epoch + }; + + let elapsed_end = std::cmp::min(epoch, deal_end); + + let num_epochs_elapsed = elapsed_end - elapsed_start; + + self.transfer_balance( + store, + &deal.client, + &deal.provider, + &(deal.storage_price_per_epoch.clone() * num_epochs_elapsed), + )?; + + if ever_slashed { + // unlock client collateral and locked storage fee + let payment_remaining = deal_get_payment_remaining(&deal, state.slash_epoch.unwrap()); + // specs actors are not handling this err + self.unlock_balance( + lt, + &deal.client, + &(payment_remaining + &deal.client_collateral), + ) + .map_err(|e| ActorError::new(ExitCode::ErrIllegalArgument, e))?; + + // slash provider collateral + let slashed = deal.provider_collateral.clone(); + self.slash_balance(et, lt, &deal.provider, &slashed) + .map_err(|e| { + ActorError::new( + ExitCode::ErrIllegalState, + format!("slashing balance: {}", e), + ) + })?; + + self.delete_deal(store, deal_id)?; + return Ok((slashed, OptionalEpoch(None))); + } + + if epoch >= deal.end_epoch { + self.process_deal_expired(store, deal_id, &deal, state, lt)?; + return Ok((TokenAmount::zero(), OptionalEpoch(None))); + } + + let next: ChainEpoch = std::cmp::min(epoch + DEAL_UPDATED_INTERVAL, deal.end_epoch); + + Ok((TokenAmount::zero(), OptionalEpoch(Some(next)))) + } + fn mutate_deal_proposals(&mut self, store: &BS, f: F) -> Result<(), ActorError> + where + F: FnOnce(&mut Amt) -> Result<(), ActorError>, + BS: BlockStore, + { + let mut prop = Amt::load(&self.proposals, store) + .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; + + f(&mut prop)?; + + let r_cid = prop.flush().map_err(|e| { + ActorError::new( + ExitCode::ErrIllegalState, + format!("flushing deal proposals set failed: {}", e), + ) + })?; + + self.proposals = r_cid; + Ok(()) + } + + fn delete_deal(&mut self, store: &BS, deal_id: DealID) -> Result<(), ActorError> + where + BS: BlockStore, + { + self.mutate_deal_proposals(store, |props: &mut Amt| { + props.delete(deal_id).map_err(|e| { + ActorError::new( + ExitCode::ErrPlaceholder, + format!("failed to delete deal: {}", e), + ) + })?; + Ok(()) + })?; + + Ok(()) + } + + /// Deal start deadline elapsed without appearing in a proven sector. + /// Delete deal, slash a portion of provider's collateral, and unlock remaining collaterals + /// for both provider and client. + pub(super) fn process_deal_init_timed_out( + &mut self, + store: &BS, + lt: &mut BalanceTable, + et: &mut BalanceTable, + deal_id: DealID, + deal: &DealProposal, + state: DealState, + ) -> Result + where + BS: BlockStore, + { + assert!( + state.sector_start_epoch.is_none(), + "Sector start epoch must be undefined" + ); + + // specs actors not handling this err + self.unlock_balance(lt, &deal.client, &deal.client_balance_requirement()) + .map_err(|e| ActorError::new(ExitCode::ErrIllegalArgument, e))?; + + let amount_slashed = + collateral_penalty_for_deal_activation_missed(deal.provider_collateral.clone()); + let amount_remaining = deal.provider_balance_requirement() - &amount_slashed; + + self.slash_balance(et, lt, &deal.provider, &amount_slashed) + .map_err(|e| { + ActorError::new( + ExitCode::ErrIllegalState, + format!("failed to slash balance: {}", e), + ) + })?; + + // specs actors not handling this err + self.unlock_balance(lt, &deal.provider, &amount_remaining) + .map_err(|e| ActorError::new(ExitCode::ErrIllegalArgument, e))?; + + self.delete_deal(store, deal_id)?; + Ok(amount_slashed) + } + + fn process_deal_expired( + &mut self, + store: &BS, + deal_id: DealID, + deal: &DealProposal, + state: DealState, + lt: &mut BalanceTable, + ) -> Result<(), ActorError> + where + BS: BlockStore, + { + assert!( + state.sector_start_epoch.is_some(), + "Sector start epoch must be initialized at this point" + ); + + self.unlock_balance(lt, &deal.provider, &deal.provider_collateral) + .map_err(|e| ActorError::new(ExitCode::ErrIllegalArgument, e))?; + + self.unlock_balance(lt, &deal.client, &deal.client_collateral) + .map_err(|e| ActorError::new(ExitCode::ErrIllegalArgument, e))?; + + self.delete_deal(store, deal_id) + } + + pub(super) fn generate_storage_deal_id(&mut self) -> DealID { + let ret = self.next_id; + self.next_id += 1; + ret + } + //////////////////////////////////////////////////////////////////////////////// // Balance table operations //////////////////////////////////////////////////////////////////////////////// @@ -56,10 +263,15 @@ impl State { a: &Address, amount: TokenAmount, ) -> Result<(), String> { - let mut bt = BalanceTable::from_root(store, &self.escrow_table)?; - bt.add_create(a, amount)?; + mutate_balance_table( + store, + &mut self.escrow_table, + |et: &mut BalanceTable| { + et.add_create(a, amount)?; + Ok(()) + }, + )?; - self.escrow_table = bt.root()?; Ok(()) } pub fn add_locked_balance( @@ -68,13 +280,18 @@ impl State { a: &Address, amount: TokenAmount, ) -> Result<(), String> { - let mut bt = BalanceTable::from_root(store, &self.locked_table)?; - bt.add_create(a, amount)?; + mutate_balance_table( + store, + &mut self.locked_table, + |lt: &mut BalanceTable| { + lt.add_create(a, amount)?; + Ok(()) + }, + )?; - self.locked_table = bt.root()?; Ok(()) } - pub fn get_escrow_balance( + fn get_escrow_balance( &self, store: &BS, a: &Address, @@ -111,60 +328,50 @@ impl State { }) } - pub(super) fn maybe_lock_balance( + fn maybe_lock_balance( &mut self, store: &BS, addr: &Address, amount: &TokenAmount, ) -> Result<(), ActorError> { let prev_locked = self.get_locked_balance(store, addr)?; - let escrow_balance = self.get_locked_balance(store, addr)?; + let escrow_balance = self.get_escrow_balance(store, addr)?; if &prev_locked + amount > escrow_balance { return Err(ActorError::new( ExitCode::ErrInsufficientFunds, format!( - "not enough balance to lock for addr {}: {} < {} + {}", - addr, escrow_balance, prev_locked, amount + "not enough balance to lock for addr {}: {} < {}", + addr, + prev_locked + amount, + escrow_balance ), )); } - let mut bt = BalanceTable::from_root(store, &self.locked_table) - .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; - bt.add(addr, amount).map_err(|e| { - ActorError::new( - ExitCode::ErrIllegalState, - format!("adding locked balance {}", e), - ) - })?; - self.locked_table = bt - .root() - .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; + mutate_balance_table( + store, + &mut self.locked_table, + |lt: &mut BalanceTable| { + lt.add(addr, amount)?; + Ok(()) + }, + ) + .map_err(|e| ActorError::new(ExitCode::ErrPlaceholder, e))?; + Ok(()) } - - pub(super) fn unlock_balance( + fn unlock_balance( &mut self, - store: &BS, + lt: &mut BalanceTable, addr: &Address, amount: &TokenAmount, - ) -> Result<(), ActorError> { - let mut bt = BalanceTable::from_root(store, &self.locked_table) - .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; + ) -> Result<(), String> { + lt.must_subtract(addr, amount)?; - bt.must_subtract(addr, amount).map_err(|e| { - ActorError::new( - ExitCode::ErrIllegalState, - format!("subtracting from locked balance: {}", e), - ) - })?; - self.locked_table = bt - .root() - .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; Ok(()) } - - pub(super) fn transfer_balance( + /// move funds from locked in client to available in provider + fn transfer_balance( &mut self, store: &BS, from_addr: &Address, @@ -186,7 +393,7 @@ impl State { lt.must_subtract(from_addr, &amount).map_err(|e| { ActorError::new( ExitCode::ErrIllegalState, - format!("subtract from escrow: {}", e), + format!("subtract from locked: {}", e), ) })?; @@ -205,268 +412,20 @@ impl State { Ok(()) } - pub(super) fn slash_balance( + fn slash_balance( &mut self, - store: &BS, + et: &mut BalanceTable, + lt: &mut BalanceTable, addr: &Address, amount: &TokenAmount, - ) -> Result<(), ActorError> { - let mut et = BalanceTable::from_root(store, &self.escrow_table) - .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; - let mut lt = BalanceTable::from_root(store, &self.locked_table) - .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; - + ) -> Result<(), String> { // Subtract from locked and escrow tables - et.must_subtract(addr, &amount).map_err(|e| { - ActorError::new( - ExitCode::ErrIllegalState, - format!("subtract from escrow: {}", e), - ) - })?; - lt.must_subtract(addr, &amount).map_err(|e| { - ActorError::new( - ExitCode::ErrIllegalState, - format!("subtract from escrow: {}", e), - ) - })?; - - // Update locked and escrow roots - self.locked_table = lt - .root() - .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; - self.escrow_table = et - .root() - .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; - Ok(()) - } - - //////////////////////////////////////////////////////////////////////////////// - // Deal state operations - //////////////////////////////////////////////////////////////////////////////// - - pub(super) fn update_pending_deal_states_for_party( - &mut self, - rt: &RT, - addr: &Address, - ) -> Result - where - BS: BlockStore, - RT: Runtime, - { - // TODO check if rt curr_epoch can be 0 - let epoch = rt.curr_epoch() - 1; - let dbp = SetMultimap::from_root(rt.store(), &self.deal_ids_by_party) - .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; - - let mut extracted_ids = Vec::new(); - dbp.for_each(addr, |id| { - extracted_ids.push(id); - Ok(()) - }) - .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e))?; - - self.update_pending_deal_states(rt.store(), extracted_ids, epoch) - } - - pub(super) fn update_pending_deal_states( - &mut self, - store: &BS, - deal_ids: Vec, - epoch: ChainEpoch, - ) -> Result - where - BS: BlockStore, - { - let mut amount_slashed_total = TokenAmount::zero(); - - for deal in deal_ids { - amount_slashed_total += self.update_pending_deal_state(store, deal, epoch)?; - } - - Ok(amount_slashed_total) - } - - pub(super) fn update_pending_deal_state( - &mut self, - store: &BS, - deal_id: DealID, - epoch: ChainEpoch, - ) -> Result - where - BS: BlockStore, - { - let deal = self.must_get_deal(store, deal_id)?; - let mut state = self.must_get_deal_state(store, deal_id)?; - - let ever_updated = state.last_updated_epoch.is_some(); - let ever_slashed = state.slash_epoch.is_some(); - - if ever_updated && state.last_updated_epoch.unwrap() > epoch { - return Err(ActorError::new( - ExitCode::ErrIllegalState, - "Deal was updated in the future".to_owned(), - )); - } - - if state.sector_start_epoch.is_none() { - if epoch > deal.start_epoch { - return self.process_deal_init_timed_out(store, deal_id, deal, state); - } - return Ok(TokenAmount::zero()); - } - - assert!( - deal.start_epoch <= epoch, - "Deal start cannot exceed current epoch" - ); - - let deal_end = if ever_slashed { - assert!( - state.slash_epoch.unwrap() <= deal.end_epoch, - "Epoch slashed must be less or equal to the end epoch" - ); - state.slash_epoch.unwrap() - } else { - deal.end_epoch - }; - - let elapsed_start = if ever_updated && state.last_updated_epoch.unwrap() > deal.start_epoch - { - state.last_updated_epoch.unwrap() - } else { - deal.start_epoch - }; - - let elapsed_end = if epoch < deal_end { epoch } else { deal_end }; - - let num_epochs_elapsed = elapsed_end - elapsed_start; - - self.transfer_balance( - store, - &deal.client, - &deal.provider, - &(deal.storage_price_per_epoch.clone() * num_epochs_elapsed), - )?; - - if ever_slashed { - let payment_remaining = deal_get_payment_remaining(&deal, state.slash_epoch.unwrap()); - self.unlock_balance( - store, - &deal.client, - &(payment_remaining + &deal.client_collateral), - )?; + et.must_subtract(addr, &amount)?; + lt.must_subtract(addr, &amount)?; - let slashed = deal.provider_collateral.clone(); - self.slash_balance(store, &deal.provider, &slashed)?; - - self.delete_deal(store, deal_id, deal)?; - return Ok(slashed); - } - - if epoch >= deal.end_epoch { - self.process_deal_expired(store, deal_id, deal, state)?; - return Ok(TokenAmount::zero()); - } - - state.last_updated_epoch = OptionalEpoch(Some(epoch)); - - // Update states array - let mut states = Amt::::load(&self.states, store) - .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; - states - .set(deal_id, state) - .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; - self.states = states - .flush() - .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; - Ok(TokenAmount::zero()) - } - - pub(super) fn delete_deal( - &mut self, - store: &BS, - deal_id: DealID, - deal: DealProposal, - ) -> Result<(), ActorError> - where - BS: BlockStore, - { - // let deal = self.must_get_deal(store, deal_id)?; - - let mut proposals = Amt::::load(&self.proposals, store) - .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; - proposals - .delete(deal_id) - .map_err(|e| ActorError::new(ExitCode::ErrPlaceholder, e.into()))?; - - let mut dbp = SetMultimap::from_root(store, &self.deal_ids_by_party) - .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; - dbp.remove(&deal.client, deal_id) - .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e))?; - - // Update roots of update - self.proposals = proposals - .flush() - .map_err(|e| ActorError::new(ExitCode::ErrPlaceholder, e.into()))?; - self.deal_ids_by_party = dbp - .root() - .map_err(|e| ActorError::new(ExitCode::ErrPlaceholder, e.into()))?; Ok(()) } - pub(super) fn process_deal_init_timed_out( - &mut self, - store: &BS, - deal_id: DealID, - deal: DealProposal, - state: DealState, - ) -> Result - where - BS: BlockStore, - { - assert!( - state.sector_start_epoch.is_none(), - "Sector start epoch must be undefined" - ); - self.unlock_balance(store, &deal.client, &deal.client_balance_requirement())?; - - let amount_slashed = - collateral_penalty_for_deal_activation_missed(deal.provider_collateral.clone()); - let amount_remainging = deal.provider_balance_requirement() - &amount_slashed; - - self.slash_balance(store, &deal.provider, &amount_slashed)?; - self.unlock_balance(store, &deal.provider, &amount_remainging)?; - self.delete_deal(store, deal_id, deal)?; - Ok(amount_slashed) - } - - pub(super) fn process_deal_expired( - &mut self, - store: &BS, - deal_id: DealID, - deal: DealProposal, - state: DealState, - ) -> Result<(), ActorError> - where - BS: BlockStore, - { - assert!( - state.sector_start_epoch.is_some(), - "Sector start epoch must be initialized at this point" - ); - - self.unlock_balance(store, &deal.provider, &deal.provider_collateral)?; - self.unlock_balance(store, &deal.client, &deal.client_collateral)?; - - self.delete_deal(store, deal_id, deal) - } - #[allow(dead_code)] - pub(super) fn generate_storage_deal_id(&mut self) -> DealID { - let ret = self.next_id; - self.next_id += 1; - ret - } - //////////////////////////////////////////////////////////////////////////////// // Method utility functions //////////////////////////////////////////////////////////////////////////////// @@ -493,30 +452,6 @@ impl State { ) })?) } - - pub(super) fn must_get_deal_state( - &self, - store: &BS, - deal_id: DealID, - ) -> Result { - let states = Amt::load(&self.states, store) - .map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.into()))?; - Ok(states - .get(deal_id) - .map_err(|e| { - ActorError::new( - ExitCode::ErrIllegalState, - format!("get deal state for id {}: {}", deal_id, e), - ) - })? - .ok_or_else(|| { - ActorError::new( - ExitCode::ErrIllegalState, - format!("deal state not found for id {}", deal_id), - ) - })?) - } - pub(super) fn lock_balance_or_abort( &mut self, store: &BS, @@ -534,6 +469,19 @@ impl State { } } +fn mutate_balance_table(store: &BS, c: &mut Cid, f: F) -> Result<(), String> +where + F: FnOnce(&mut BalanceTable) -> Result<(), String>, + BS: BlockStore, +{ + let mut t = BalanceTable::from_root(store, &c)?; + + f(&mut t)?; + + *c = t.root()?; + Ok(()) +} + fn deal_get_payment_remaining(deal: &DealProposal, epoch: ChainEpoch) -> TokenAmount { assert!( epoch <= deal.end_epoch, diff --git a/vm/actor/src/builtin/singletons.rs b/vm/actor/src/builtin/singletons.rs index 3ccd5c49e5b1..9a293ff0db12 100644 --- a/vm/actor/src/builtin/singletons.rs +++ b/vm/actor/src/builtin/singletons.rs @@ -11,6 +11,8 @@ lazy_static! { pub static ref CRON_ACTOR_ADDR: Address = Address::new_id(3); pub static ref STORAGE_POWER_ACTOR_ADDR: Address = Address::new_id(4); pub static ref STORAGE_MARKET_ACTOR_ADDR: Address = Address::new_id(5); + pub static ref VERIFIED_REGISTRY_ACTOR_ADDR: Address = Address::new_id(6); + // Distinguished AccountActor that is the destination of all burnt funds. pub static ref BURNT_FUNDS_ACTOR_ADDR: Address = Address::new_id(99); } diff --git a/vm/actor/src/lib.rs b/vm/actor/src/lib.rs index 2e3349e1cad6..d579c6d93962 100644 --- a/vm/actor/src/lib.rs +++ b/vm/actor/src/lib.rs @@ -40,9 +40,9 @@ fn make_map(store: &'_ BS) -> Hamt<'_, BytesKey, BS> { Hamt::new_with_bit_width(store, HAMT_BIT_WIDTH) } -pub fn u64_key(d: DealID) -> BytesKey { +pub fn u64_key(k: u64) -> BytesKey { let mut bz = unsigned_varint::encode::u64_buffer(); - unsigned_varint::encode::u64(d, &mut bz); + unsigned_varint::encode::u64(k, &mut bz); bz.to_vec().into() } diff --git a/vm/actor/src/util/set.rs b/vm/actor/src/util/set.rs index e150576b0c9f..045ad50a0f56 100644 --- a/vm/actor/src/util/set.rs +++ b/vm/actor/src/util/set.rs @@ -5,6 +5,7 @@ use crate::{BytesKey, EmptyType, EMPTY_VALUE, HAMT_BIT_WIDTH}; use cid::Cid; use ipld_blockstore::BlockStore; use ipld_hamt::{Error, Hamt}; +use std::error::Error as StdError; /// Set is a Hamt with empty values for the purpose of acting as a hash set. #[derive(Debug)] @@ -58,16 +59,20 @@ where } /// Iterates through all keys in the set. - pub fn for_each(&self, mut f: F) -> Result<(), String> + pub fn for_each(&self, mut f: F) -> Result<(), Box> where - F: FnMut(&BytesKey) -> Result<(), String>, + F: FnMut(&BytesKey) -> Result<(), Box>, { // Calls the for each function on the hamt with ignoring the value - self.0.for_each(|s, _: EmptyType| f(s)) + // TODO there are no actor errors used in the generic function yet, but the HAMT for_each + // iterator should be Box to not convert to String and lose exit code + Ok(self + .0 + .for_each(|s, _: EmptyType| f(s).map_err(|e| e.to_string()))?) } /// Collects all keys from the set into a vector. - pub fn collect_keys(&self) -> Result, String> { + pub fn collect_keys(&self) -> Result, Box> { let mut ret_keys = Vec::new(); self.for_each(|k| { diff --git a/vm/actor/src/util/set_multimap.rs b/vm/actor/src/util/set_multimap.rs index d3d7b37d5ac7..505b8c1f9b8e 100644 --- a/vm/actor/src/util/set_multimap.rs +++ b/vm/actor/src/util/set_multimap.rs @@ -3,11 +3,12 @@ use super::Set; use crate::{parse_uint_key, u64_key, BytesKey, DealID, HAMT_BIT_WIDTH}; -use address::Address; use cid::Cid; +use clock::ChainEpoch; use ipld_blockstore::BlockStore; use ipld_hamt::{Error, Hamt}; use std::borrow::Borrow; +use std::error::Error as StdError; /// SetMultimap is a hamt with values that are also a hamt but are of the set variant. /// This allows hash sets to be indexable by an address. @@ -33,7 +34,7 @@ where } /// Puts the DealID in the hash set of the key. - pub fn put(&mut self, key: &Address, value: DealID) -> Result<(), String> { + pub fn put(&mut self, key: ChainEpoch, value: DealID) -> Result<(), String> { // Get construct amt from retrieved cid or create new let mut set = self.get(key)?.unwrap_or_else(|| Set::new(self.0.store())); @@ -43,13 +44,13 @@ where let new_root = set.root()?; // Set hamt node to set new root - Ok(self.0.set(key.to_bytes().into(), &new_root)?) + Ok(self.0.set(u64_key(key), &new_root)?) } /// Gets the set at the given index of the `SetMultimap` #[inline] - pub fn get(&self, key: &Address) -> Result>, String> { - match self.0.get(&key.to_bytes())? { + pub fn get(&self, key: ChainEpoch) -> Result>, String> { + match self.0.get(&u64_key(key))? { Some(cid) => Ok(Some(Set::from_root(self.0.store(), &cid)?)), None => Ok(None), } @@ -57,7 +58,7 @@ where /// Removes a DealID from a key hash set. #[inline] - pub fn remove(&mut self, key: &Address, v: DealID) -> Result<(), String> { + pub fn remove(&mut self, key: ChainEpoch, v: DealID) -> Result<(), String> { // Get construct amt from retrieved cid and return if no set exists let mut set = match self.get(key)? { Some(s) => s, @@ -69,22 +70,22 @@ where // Save and calculate new root let new_root = set.root()?; - Ok(self.0.set(key.to_bytes().into(), &new_root)?) + Ok(self.0.set(u64_key(key), &new_root)?) } /// Removes set at index. #[inline] - pub fn remove_all(&mut self, key: &Address) -> Result<(), String> { + pub fn remove_all(&mut self, key: ChainEpoch) -> Result<(), String> { // Remove entry from table - self.0.delete(&key.to_bytes())?; + self.0.delete(&u64_key(key))?; Ok(()) } /// Iterates through keys and converts them to a DealID to call a function on each. - pub fn for_each(&self, key: &Address, mut f: F) -> Result<(), String> + pub fn for_each(&self, key: ChainEpoch, mut f: F) -> Result<(), Box> where - F: FnMut(DealID) -> Result<(), String>, + F: FnMut(DealID) -> Result<(), Box>, { // Get construct amt from retrieved cid and return if no set exists let set = match self.get(key)? { diff --git a/vm/actor/tests/common/mod.rs b/vm/actor/tests/common/mod.rs index ca5d3cdc1e71..87d43e7de8a1 100644 --- a/vm/actor/tests/common/mod.rs +++ b/vm/actor/tests/common/mod.rs @@ -4,7 +4,7 @@ use actor::{ self, ACCOUNT_ACTOR_CODE_ID, CRON_ACTOR_CODE_ID, INIT_ACTOR_CODE_ID, MARKET_ACTOR_CODE_ID, MINER_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID, PAYCH_ACTOR_CODE_ID, POWER_ACTOR_CODE_ID, - REWARD_ACTOR_CODE_ID, SYSTEM_ACTOR_CODE_ID, + REWARD_ACTOR_CODE_ID, SYSTEM_ACTOR_CODE_ID, VERIFIED_ACTOR_CODE_ID, }; use address::Address; use cid::{multihash::Blake2b256, Cid}; @@ -171,6 +171,9 @@ impl<'a, BS: BlockStore> MockRuntime<'a, BS> { x if x == &*REWARD_ACTOR_CODE_ID => { actor::reward::Actor.invoke_method(self, method_num, params) } + x if x == &*VERIFIED_ACTOR_CODE_ID => { + actor::verifreg::Actor.invoke_method(self, method_num, params) + } _ => Err(ActorError::new( ExitCode::SysErrForbidden, "invalid method id".to_owned(), diff --git a/vm/actor/tests/set_multimap_test.rs b/vm/actor/tests/set_multimap_test.rs index 4f79704badf6..2785c4a13cde 100644 --- a/vm/actor/tests/set_multimap_test.rs +++ b/vm/actor/tests/set_multimap_test.rs @@ -2,26 +2,26 @@ // SPDX-License-Identifier: Apache-2.0, MIT use actor::{u64_key, SetMultimap}; -use address::Address; +use clock::ChainEpoch; #[test] fn put_remove() { let store = db::MemoryDB::default(); let mut smm = SetMultimap::new(&store); - let addr = Address::new_id(100); - assert_eq!(smm.get(&addr), Ok(None)); + let epoch: ChainEpoch = 100; + assert_eq!(smm.get(epoch), Ok(None)); - smm.put(&addr, 8).unwrap(); - smm.put(&addr, 2).unwrap(); - smm.remove(&addr, 2).unwrap(); + smm.put(epoch, 8).unwrap(); + smm.put(epoch, 2).unwrap(); + smm.remove(epoch, 2).unwrap(); - let set = smm.get(&addr).unwrap().unwrap(); + let set = smm.get(epoch).unwrap().unwrap(); assert_eq!(set.has(&u64_key(8)), Ok(true)); assert_eq!(set.has(&u64_key(2)), Ok(false)); - smm.remove_all(&addr).unwrap(); - assert_eq!(smm.get(&addr), Ok(None)); + smm.remove_all(epoch).unwrap(); + assert_eq!(smm.get(epoch), Ok(None)); } #[test] @@ -29,16 +29,16 @@ fn for_each() { let store = db::MemoryDB::default(); let mut smm = SetMultimap::new(&store); - let addr = Address::new_id(100); - assert_eq!(smm.get(&addr), Ok(None)); + let epoch: ChainEpoch = 100; + assert_eq!(smm.get(epoch), Ok(None)); - smm.put(&addr, 8).unwrap(); - smm.put(&addr, 3).unwrap(); - smm.put(&addr, 2).unwrap(); - smm.put(&addr, 8).unwrap(); + smm.put(epoch, 8).unwrap(); + smm.put(epoch, 3).unwrap(); + smm.put(epoch, 2).unwrap(); + smm.put(epoch, 8).unwrap(); let mut vals: Vec = Vec::new(); - smm.for_each(&addr, |i| { + smm.for_each(epoch, |i| { vals.push(i); Ok(()) }) diff --git a/vm/interpreter/src/default_runtime.rs b/vm/interpreter/src/default_runtime.rs index 05dd3d5165e9..08528d65fc30 100644 --- a/vm/interpreter/src/default_runtime.rs +++ b/vm/interpreter/src/default_runtime.rs @@ -8,7 +8,7 @@ use super::ChainRand; use actor::{ self, account, ACCOUNT_ACTOR_CODE_ID, CRON_ACTOR_CODE_ID, INIT_ACTOR_CODE_ID, MARKET_ACTOR_CODE_ID, MINER_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID, PAYCH_ACTOR_CODE_ID, - POWER_ACTOR_CODE_ID, REWARD_ACTOR_CODE_ID, SYSTEM_ACTOR_CODE_ID, + POWER_ACTOR_CODE_ID, REWARD_ACTOR_CODE_ID, SYSTEM_ACTOR_CODE_ID, VERIFIED_ACTOR_CODE_ID, }; use address::{Address, Protocol}; use byteorder::{BigEndian, WriteBytesExt}; @@ -476,6 +476,9 @@ where x if x == *REWARD_ACTOR_CODE_ID => { actor::reward::Actor.invoke_method(runtime, method_num, msg.params()) } + x if x == *VERIFIED_ACTOR_CODE_ID => { + actor::verifreg::Actor.invoke_method(runtime, method_num, msg.params()) + } _ => Err(ActorError::new( ExitCode::SysErrorIllegalActor, format!("no code for actor at address {}", msg.to()), diff --git a/vm/src/code.rs b/vm/src/code.rs index 20674f29b902..d3d6254eb556 100644 --- a/vm/src/code.rs +++ b/vm/src/code.rs @@ -10,10 +10,14 @@ pub enum CodeID { Init, Cron, Account, + Reward, PaymentChannel, StoragePower, - StorageMiner, StorageMarket, + StorageMiner, + System, + Verifreg, + Multisig, CustomCode(Cid), } @@ -30,7 +34,13 @@ impl CodeID { /// Returns true if cid is singleton Actor pub fn is_singleton(&self) -> bool { match *self { - CodeID::StorageMarket | CodeID::Init | CodeID::StoragePower => true, + CodeID::StorageMarket + | CodeID::Init + | CodeID::StoragePower + | CodeID::Cron + | CodeID::Reward + | CodeID::System + | CodeID::Verifreg => true, _ => false, } } @@ -45,12 +55,16 @@ mod test { fn builtin_checks() { // Tests all builtins will return true assert!(CodeID::Init.is_builtin()); + assert!(CodeID::System.is_builtin()); assert!(CodeID::StorageMarket.is_builtin()); assert!(CodeID::StoragePower.is_builtin()); assert!(CodeID::Cron.is_builtin()); assert!(CodeID::Account.is_builtin()); assert!(CodeID::PaymentChannel.is_builtin()); assert!(CodeID::StorageMiner.is_builtin()); + assert!(CodeID::Verifreg.is_builtin()); + assert!(CodeID::Multisig.is_builtin()); + assert!(CodeID::Reward.is_builtin()); assert!(!CodeID::CustomCode(Cid::default()).is_builtin()); } @@ -61,11 +75,15 @@ mod test { assert!(CodeID::Init.is_singleton()); assert!(CodeID::StorageMarket.is_singleton()); assert!(CodeID::StoragePower.is_singleton()); + assert!(CodeID::Verifreg.is_singleton()); + assert!(CodeID::Reward.is_singleton()); + assert!(CodeID::System.is_singleton()); + assert!(CodeID::Cron.is_singleton()); // non-singletons - assert!(!CodeID::Cron.is_singleton()); assert!(!CodeID::Account.is_singleton()); assert!(!CodeID::PaymentChannel.is_singleton()); assert!(!CodeID::StorageMiner.is_singleton()); + assert!(!CodeID::Multisig.is_singleton()); assert!(!CodeID::CustomCode(Cid::default()).is_singleton()); } }