diff --git a/api/api_full.go b/api/api_full.go index 80edf385b..5ff417dfd 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -122,6 +122,8 @@ type FullNode interface { StateMinerSectorCount(context.Context, address.Address, *types.TipSet) (MinerSectors, error) StateCompute(context.Context, uint64, []*types.Message, *types.TipSet) (cid.Cid, error) + MsigGetAvailableBalance(context.Context, address.Address, *types.TipSet) (types.BigInt, error) + MarketEnsureAvailable(context.Context, address.Address, types.BigInt) error // MarketFreeBalance diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index 1a6e09421..57a0d9707 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -118,6 +118,8 @@ type FullNodeStruct struct { StateListMessages func(ctx context.Context, match *types.Message, ts *types.TipSet, toht uint64) ([]cid.Cid, error) `perm:"read"` StateCompute func(context.Context, uint64, []*types.Message, *types.TipSet) (cid.Cid, error) `perm:"read"` + MsigGetAvailableBalance func(context.Context, address.Address, *types.TipSet) (types.BigInt, error) `perm:"read"` + MarketEnsureAvailable func(context.Context, address.Address, types.BigInt) error `perm:"sign"` PaychGet func(ctx context.Context, from, to address.Address, ensureFunds types.BigInt) (*api.ChannelInfo, error) `perm:"sign"` @@ -477,6 +479,10 @@ func (c *FullNodeStruct) StateCompute(ctx context.Context, height uint64, msgs [ return c.Internal.StateCompute(ctx, height, msgs, ts) } +func (c *FullNodeStruct) MsigGetAvailableBalance(ctx context.Context, a address.Address, ts *types.TipSet) (types.BigInt, error) { + return c.Internal.MsigGetAvailableBalance(ctx, a, ts) +} + func (c *FullNodeStruct) MarketEnsureAvailable(ctx context.Context, addr address.Address, amt types.BigInt) error { return c.Internal.MarketEnsureAvailable(ctx, addr, amt) } diff --git a/node/impl/full/state.go b/node/impl/full/state.go index 3ba0dfbf8..c505c6efc 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -3,6 +3,7 @@ package full import ( "bytes" "context" + "fmt" "strconv" "github.com/filecoin-project/go-amt-ipld" @@ -404,3 +405,32 @@ func (a *StateAPI) StateListMessages(ctx context.Context, match *types.Message, func (a *StateAPI) StateCompute(ctx context.Context, height uint64, msgs []*types.Message, ts *types.TipSet) (cid.Cid, error) { return stmgr.ComputeState(ctx, a.StateManager, height, msgs, ts) } + +func (a *StateAPI) MsigGetAvailableBalance(ctx context.Context, addr address.Address, ts *types.TipSet) (types.BigInt, error) { + if ts == nil { + ts = a.Chain.GetHeaviestTipSet() + } + + var st actors.MultiSigActorState + act, err := a.StateManager.LoadActorState(ctx, addr, &st, ts) + if err != nil { + return types.EmptyInt, xerrors.Errorf("failed to load multisig actor state: %w", err) + } + + if act.Code != actors.MultisigCodeCid { + return types.EmptyInt, fmt.Errorf("given actor was not a multisig") + } + + if st.UnlockDuration == 0 { + return act.Balance, nil + } + + offset := ts.Height() - st.StartingBlock + if offset > st.UnlockDuration { + return act.Balance, nil + } + + minBalance := types.BigDiv(st.InitialBalance, types.NewInt(st.UnlockDuration)) + minBalance = types.BigMul(minBalance, types.NewInt(offset)) + return types.BigSub(act.Balance, minBalance), nil +}