Skip to content

Commit

Permalink
feat(rpc): implement Filecoin.MsigGetVested and Filecoin.MsigGetVesti…
Browse files Browse the repository at this point in the history
…ngSchedule (#4304)
  • Loading branch information
sudo-shashank authored May 10, 2024
1 parent ea05438 commit 10d9224
Show file tree
Hide file tree
Showing 9 changed files with 229 additions and 68 deletions.
3 changes: 2 additions & 1 deletion src/rpc/auth_layer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use crate::auth::{verify_token, JWT_IDENTIFIER};
use crate::key_management::KeyStore;
use crate::rpc::{
auth, beacon, chain, common, eth, gas, miner, mpool, net, node, state, sync, wallet,
auth, beacon, chain, common, eth, gas, miner, mpool, msig, net, node, state, sync, wallet,
Permission, RpcMethod as _, CANCEL_METHOD_NAME,
};
use ahash::{HashMap, HashMapExt as _};
Expand Down Expand Up @@ -43,6 +43,7 @@ static METHOD_NAME2REQUIRED_PERMISSION: Lazy<HashMap<&str, Permission>> = Lazy::
sync::for_each_method!(insert);
wallet::for_each_method!(insert);
eth::for_each_method!(insert);
msig::for_each_method!(insert);

access.insert(chain::CHAIN_NOTIFY, Permission::Read);
access.insert(CANCEL_METHOD_NAME, Permission::Read);
Expand Down
149 changes: 149 additions & 0 deletions src/rpc/methods/msig.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// Copyright 2019-2024 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use crate::rpc::error::ServerError;
use crate::rpc::types::ApiTipsetKey;
use crate::rpc::types::*;
use crate::rpc::{ApiVersion, Ctx, Permission, RpcMethod};
use crate::shim::actors::multisig::MultisigExt;
use crate::shim::{address::Address, econ::TokenAmount};
use fil_actor_interface::multisig;
use fvm_ipld_blockstore::Blockstore;
use num_bigint::BigInt;

macro_rules! for_each_method {
($callback:ident) => {
$callback!(crate::rpc::msig::MsigGetAvailableBalance);
$callback!(crate::rpc::msig::MsigGetPending);
$callback!(crate::rpc::msig::MsigGetVested);
$callback!(crate::rpc::msig::MsigGetVestingSchedule);
};
}
pub(crate) use for_each_method;

pub enum MsigGetAvailableBalance {}

impl RpcMethod<2> for MsigGetAvailableBalance {
const NAME: &'static str = "Filecoin.MsigGetAvailableBalance";
const PARAM_NAMES: [&'static str; 2] = ["address", "tipset_key"];
const API_VERSION: ApiVersion = ApiVersion::V0;
const PERMISSION: Permission = Permission::Read;

type Params = (Address, ApiTipsetKey);
type Ok = TokenAmount;

async fn handle(
ctx: Ctx<impl Blockstore>,
(address, ApiTipsetKey(tsk)): Self::Params,
) -> Result<Self::Ok, ServerError> {
let ts = ctx.chain_store.load_required_tipset_or_heaviest(&tsk)?;
let height = ts.epoch();
let actor = ctx
.state_manager
.get_required_actor(&address, *ts.parent_state())?;
let actor_balance = TokenAmount::from(&actor.balance);
let ms = multisig::State::load(ctx.store(), actor.code, actor.state)?;
let locked_balance = ms.locked_balance(height)?.into();
let avail_balance = &actor_balance - locked_balance;
Ok(avail_balance)
}
}

pub enum MsigGetPending {}

impl RpcMethod<2> for MsigGetPending {
const NAME: &'static str = "Filecoin.MsigGetPending";
const PARAM_NAMES: [&'static str; 2] = ["address", "tipset_key"];
const API_VERSION: ApiVersion = ApiVersion::V0;
const PERMISSION: Permission = Permission::Read;

type Params = (Address, ApiTipsetKey);
type Ok = Vec<Transaction>;

async fn handle(
ctx: Ctx<impl Blockstore>,
(address, ApiTipsetKey(tsk)): Self::Params,
) -> Result<Self::Ok, ServerError> {
let ts = ctx.chain_store.load_required_tipset_or_heaviest(&tsk)?;
let actor = ctx
.state_manager
.get_required_actor(&address, *ts.parent_state())?;
let ms = multisig::State::load(ctx.store(), actor.code, actor.state)?;
let txns = ms
.get_pending_txn(ctx.store())?
.iter()
.map(|txn| Transaction {
id: txn.id,
to: txn.to.into(),
value: txn.value.clone().into(),
method: txn.method,
params: txn.params.clone(),
approved: txn.approved.iter().map(|item| item.into()).collect(),
})
.collect();
Ok(txns)
}
}

pub enum MsigGetVested {}
impl RpcMethod<3> for MsigGetVested {
const NAME: &'static str = "Filecoin.MsigGetVested";
const PARAM_NAMES: [&'static str; 3] = ["address", "start_tsk", "end_tsk"];
const API_VERSION: ApiVersion = ApiVersion::V0;
const PERMISSION: Permission = Permission::Read;

type Params = (Address, ApiTipsetKey, ApiTipsetKey);
type Ok = BigInt;

async fn handle(
ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
(addr, ApiTipsetKey(start_tsk), ApiTipsetKey(end_tsk)): Self::Params,
) -> Result<Self::Ok, ServerError> {
let start_ts = ctx
.chain_store
.load_required_tipset_or_heaviest(&start_tsk)?;
let end_ts = ctx.chain_store.load_required_tipset_or_heaviest(&end_tsk)?;

match start_ts.epoch().cmp(&end_ts.epoch()) {
std::cmp::Ordering::Greater => Err(ServerError::internal_error(
"start tipset is after end tipset",
None,
)),
std::cmp::Ordering::Equal => Ok(BigInt::from(0)),
std::cmp::Ordering::Less => {
let msig_actor = ctx
.state_manager
.get_required_actor(&addr, *end_ts.parent_state())?;
let ms = multisig::State::load(ctx.store(), msig_actor.code, msig_actor.state)?;
let start_lb: TokenAmount = ms.locked_balance(start_ts.epoch())?.into();
let end_lb: TokenAmount = ms.locked_balance(end_ts.epoch())?.into();
Ok(start_lb.atto() - end_lb.atto())
}
}
}
}

pub enum MsigGetVestingSchedule {}
impl RpcMethod<2> for MsigGetVestingSchedule {
const NAME: &'static str = "Filecoin.MsigGetVestingSchedule";
const PARAM_NAMES: [&'static str; 2] = ["address", "tsk"];
const API_VERSION: ApiVersion = ApiVersion::V0;
const PERMISSION: Permission = Permission::Read;

type Params = (Address, ApiTipsetKey);
type Ok = MsigVesting;

async fn handle(
ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
(addr, ApiTipsetKey(tsk)): Self::Params,
) -> Result<Self::Ok, ServerError> {
let ts = ctx.chain_store.load_required_tipset_or_heaviest(&tsk)?;

let msig_actor = ctx
.state_manager
.get_required_actor(&addr, *ts.parent_state())?;
let ms = multisig::State::load(ctx.store(), msig_actor.code, msig_actor.state)?;

Ok(ms.get_vesting_schedule()?)
}
}
68 changes: 1 addition & 67 deletions src/rpc/methods/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use fil_actor_interface::market::DealState;
use fil_actor_interface::{
market, miner,
miner::{MinerInfo, MinerPower},
multisig, power, reward,
power, reward,
};
use fil_actor_miner_state::v10::{qa_power_for_weight, qa_power_max};
use fil_actors_shared::fvm_ipld_bitfield::BitField;
Expand Down Expand Up @@ -81,8 +81,6 @@ macro_rules! for_each_method {
$callback!(crate::rpc::state::StateGetRandomnessFromBeacon);
$callback!(crate::rpc::state::StateReadState);
$callback!(crate::rpc::state::StateCirculatingSupply);
$callback!(crate::rpc::state::MsigGetAvailableBalance);
$callback!(crate::rpc::state::MsigGetPending);
$callback!(crate::rpc::state::StateVerifiedClientStatus);
$callback!(crate::rpc::state::StateVMCirculatingSupplyInternal);
$callback!(crate::rpc::state::StateListMiners);
Expand Down Expand Up @@ -1247,70 +1245,6 @@ impl RpcMethod<1> for StateCirculatingSupply {
}
}

pub enum MsigGetAvailableBalance {}

impl RpcMethod<2> for MsigGetAvailableBalance {
const NAME: &'static str = "Filecoin.MsigGetAvailableBalance";
const PARAM_NAMES: [&'static str; 2] = ["address", "tipset_key"];
const API_VERSION: ApiVersion = ApiVersion::V0;
const PERMISSION: Permission = Permission::Read;

type Params = (Address, ApiTipsetKey);
type Ok = TokenAmount;

async fn handle(
ctx: Ctx<impl Blockstore>,
(address, ApiTipsetKey(tsk)): Self::Params,
) -> Result<Self::Ok, ServerError> {
let ts = ctx.chain_store.load_required_tipset_or_heaviest(&tsk)?;
let height = ts.epoch();
let actor = ctx
.state_manager
.get_required_actor(&address, *ts.parent_state())?;
let actor_balance = TokenAmount::from(&actor.balance);
let ms = multisig::State::load(ctx.store(), actor.code, actor.state)?;
let locked_balance = ms.locked_balance(height)?.into();
let avail_balance = &actor_balance - locked_balance;
Ok(avail_balance)
}
}

pub enum MsigGetPending {}

impl RpcMethod<2> for MsigGetPending {
const NAME: &'static str = "Filecoin.MsigGetPending";
const PARAM_NAMES: [&'static str; 2] = ["address", "tipset_key"];
const API_VERSION: ApiVersion = ApiVersion::V0;
const PERMISSION: Permission = Permission::Read;

type Params = (Address, ApiTipsetKey);
type Ok = Vec<Transaction>;

async fn handle(
ctx: Ctx<impl Blockstore>,
(address, ApiTipsetKey(tsk)): Self::Params,
) -> Result<Self::Ok, ServerError> {
let ts = ctx.chain_store.load_required_tipset_or_heaviest(&tsk)?;
let actor = ctx
.state_manager
.get_required_actor(&address, *ts.parent_state())?;
let ms = multisig::State::load(ctx.store(), actor.code, actor.state)?;
let txns = ms
.get_pending_txn(ctx.store())?
.iter()
.map(|txn| Transaction {
id: txn.id,
to: txn.to.into(),
value: txn.value.clone().into(),
method: txn.method,
params: txn.params.clone(),
approved: txn.approved.iter().map(|item| item.into()).collect(),
})
.collect();
Ok(txns)
}
}

pub enum StateVerifiedClientStatus {}

impl RpcMethod<2> for StateVerifiedClientStatus {
Expand Down
3 changes: 3 additions & 0 deletions src/rpc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ pub mod prelude {
sync::for_each_method!(export);
wallet::for_each_method!(export);
eth::for_each_method!(export);
msig::for_each_method!(export);
}

/// All the methods live in their own folder
Expand Down Expand Up @@ -109,6 +110,7 @@ mod methods {
pub mod gas;
pub mod miner;
pub mod mpool;
pub mod msig;
pub mod net;
pub mod node;
pub mod state;
Expand Down Expand Up @@ -276,6 +278,7 @@ where
sync::for_each_method!(register);
wallet::for_each_method!(register);
eth::for_each_method!(register);
msig::for_each_method!(register);
module.finish()
}

Expand Down
12 changes: 12 additions & 0 deletions src/rpc/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,18 @@ pub struct ApiDealState {

lotus_json_with_self!(ApiDealState);

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "PascalCase")]
pub struct MsigVesting {
#[schemars(with = "LotusJson<BigInt>")]
#[serde(with = "crate::lotus_json")]
pub initial_balance: BigInt,
pub start_epoch: ChainEpoch,
pub unlock_duration: ChainEpoch,
}

lotus_json_with_self!(MsigVesting);

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "PascalCase")]
pub struct ApiDealProposal {
Expand Down
1 change: 1 addition & 0 deletions src/shim/actors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@

pub mod market;
pub mod miner;
pub mod multisig;
11 changes: 11 additions & 0 deletions src/shim/actors/multisig.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright 2019-2024 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

mod state;

use crate::rpc::types::MsigVesting;
use fil_actor_interface::multisig::State;

pub trait MultisigExt {
fn get_vesting_schedule(&self) -> anyhow::Result<MsigVesting>;
}
41 changes: 41 additions & 0 deletions src/shim/actors/multisig/state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright 2019-2024 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use super::*;

impl MultisigExt for State {
fn get_vesting_schedule(&self) -> anyhow::Result<MsigVesting> {
match self {
State::V8(st) => Ok(MsigVesting {
initial_balance: st.initial_balance.atto().clone(),
start_epoch: st.start_epoch,
unlock_duration: st.unlock_duration,
}),
State::V9(st) => Ok(MsigVesting {
initial_balance: st.initial_balance.atto().clone(),
start_epoch: st.start_epoch,
unlock_duration: st.unlock_duration,
}),
State::V10(st) => Ok(MsigVesting {
initial_balance: st.initial_balance.atto().clone(),
start_epoch: st.start_epoch,
unlock_duration: st.unlock_duration,
}),
State::V11(st) => Ok(MsigVesting {
initial_balance: st.initial_balance.atto().clone(),
start_epoch: st.start_epoch,
unlock_duration: st.unlock_duration,
}),
State::V12(st) => Ok(MsigVesting {
initial_balance: st.initial_balance.atto().clone(),
start_epoch: st.start_epoch,
unlock_duration: st.unlock_duration,
}),
State::V13(st) => Ok(MsigVesting {
initial_balance: st.initial_balance.atto().clone(),
start_epoch: st.start_epoch,
unlock_duration: st.unlock_duration,
}),
}
}
}
9 changes: 9 additions & 0 deletions src/tool/subcommands/api_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,15 @@ fn state_tests_with_tipset<DB: Blockstore>(
Address::new_id(18101), // msig address id
tipset.key().into(),
))?),
RpcTest::identity(MsigGetVested::request((
Address::new_id(18101), // msig address id
tipset.parents().into(),
tipset.key().into(),
))?),
RpcTest::identity(MsigGetVestingSchedule::request((
Address::new_id(18101), // msig address id
tipset.key().into(),
))?),
RpcTest::identity(StateGetBeaconEntry::request((tipset.epoch(),))?),
// Not easily verifiable by using addresses extracted from blocks as most of those yield `null`
// for both Lotus and Forest. Therefore the actor addresses are hardcoded to values that allow
Expand Down

0 comments on commit 10d9224

Please sign in to comment.