2019-08-20 16:48:33 +00:00
|
|
|
package full
|
|
|
|
|
|
|
|
import (
|
2019-10-22 19:29:05 +00:00
|
|
|
"bytes"
|
2019-08-20 16:48:33 +00:00
|
|
|
"context"
|
2020-01-30 01:23:16 +00:00
|
|
|
"fmt"
|
2019-10-22 19:29:05 +00:00
|
|
|
"strconv"
|
2019-09-03 04:36:07 +00:00
|
|
|
|
2019-11-07 07:57:10 +00:00
|
|
|
"github.com/filecoin-project/go-amt-ipld"
|
|
|
|
|
2019-09-19 20:25:18 +00:00
|
|
|
cid "github.com/ipfs/go-cid"
|
2019-09-16 20:11:17 +00:00
|
|
|
"github.com/ipfs/go-hamt-ipld"
|
|
|
|
"github.com/libp2p/go-libp2p-core/peer"
|
2019-10-22 19:29:05 +00:00
|
|
|
cbg "github.com/whyrusleeping/cbor-gen"
|
2019-09-16 20:11:17 +00:00
|
|
|
"go.uber.org/fx"
|
|
|
|
"golang.org/x/xerrors"
|
|
|
|
|
2019-12-19 20:13:17 +00:00
|
|
|
"github.com/filecoin-project/go-address"
|
2019-10-18 04:47:41 +00:00
|
|
|
"github.com/filecoin-project/lotus/api"
|
|
|
|
"github.com/filecoin-project/lotus/chain/actors"
|
|
|
|
"github.com/filecoin-project/lotus/chain/gen"
|
|
|
|
"github.com/filecoin-project/lotus/chain/state"
|
|
|
|
"github.com/filecoin-project/lotus/chain/stmgr"
|
|
|
|
"github.com/filecoin-project/lotus/chain/store"
|
|
|
|
"github.com/filecoin-project/lotus/chain/types"
|
|
|
|
"github.com/filecoin-project/lotus/chain/vm"
|
|
|
|
"github.com/filecoin-project/lotus/chain/wallet"
|
|
|
|
"github.com/filecoin-project/lotus/lib/bufbstore"
|
2019-08-20 16:48:33 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type StateAPI struct {
|
|
|
|
fx.In
|
|
|
|
|
2019-09-06 06:26:02 +00:00
|
|
|
// TODO: the wallet here is only needed because we have the MinerCreateBlock
|
|
|
|
// API attached to the state API. It probably should live somewhere better
|
|
|
|
Wallet *wallet.Wallet
|
|
|
|
|
|
|
|
StateManager *stmgr.StateManager
|
|
|
|
Chain *store.ChainStore
|
2019-08-20 16:48:33 +00:00
|
|
|
}
|
|
|
|
|
2019-11-08 18:15:13 +00:00
|
|
|
func (a *StateAPI) StateMinerSectors(ctx context.Context, addr address.Address, ts *types.TipSet) ([]*api.ChainSectorInfo, error) {
|
2019-10-27 10:27:21 +00:00
|
|
|
return stmgr.GetMinerSectorSet(ctx, a.StateManager, ts, addr)
|
2019-08-20 16:48:33 +00:00
|
|
|
}
|
|
|
|
|
2019-11-08 18:15:13 +00:00
|
|
|
func (a *StateAPI) StateMinerProvingSet(ctx context.Context, addr address.Address, ts *types.TipSet) ([]*api.ChainSectorInfo, error) {
|
2019-09-18 02:50:03 +00:00
|
|
|
return stmgr.GetMinerProvingSet(ctx, a.StateManager, ts, addr)
|
2019-08-20 16:48:33 +00:00
|
|
|
}
|
2019-08-21 16:31:14 +00:00
|
|
|
|
|
|
|
func (a *StateAPI) StateMinerPower(ctx context.Context, maddr address.Address, ts *types.TipSet) (api.MinerPower, error) {
|
2019-09-09 20:03:10 +00:00
|
|
|
mpow, tpow, err := stmgr.GetPower(ctx, a.StateManager, ts, maddr)
|
2019-08-21 16:31:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return api.MinerPower{}, err
|
|
|
|
}
|
|
|
|
|
2019-11-15 01:26:25 +00:00
|
|
|
if maddr != address.Undef {
|
|
|
|
slashed, err := stmgr.GetMinerSlashed(ctx, a.StateManager, ts, maddr)
|
|
|
|
if err != nil {
|
|
|
|
return api.MinerPower{}, err
|
|
|
|
}
|
|
|
|
if slashed != 0 {
|
|
|
|
mpow = types.NewInt(0)
|
|
|
|
}
|
2019-11-15 01:01:53 +00:00
|
|
|
}
|
|
|
|
|
2019-08-21 16:31:14 +00:00
|
|
|
return api.MinerPower{
|
|
|
|
MinerPower: mpow,
|
|
|
|
TotalPower: tpow,
|
|
|
|
}, nil
|
|
|
|
}
|
2019-09-03 04:36:07 +00:00
|
|
|
|
|
|
|
func (a *StateAPI) StateMinerWorker(ctx context.Context, m address.Address, ts *types.TipSet) (address.Address, error) {
|
2019-10-23 17:39:14 +00:00
|
|
|
return stmgr.GetMinerWorker(ctx, a.StateManager, ts, m)
|
2019-09-03 04:36:07 +00:00
|
|
|
}
|
2019-09-06 06:26:02 +00:00
|
|
|
|
2019-09-16 20:11:17 +00:00
|
|
|
func (a *StateAPI) StateMinerPeerID(ctx context.Context, m address.Address, ts *types.TipSet) (peer.ID, error) {
|
2019-09-17 08:00:38 +00:00
|
|
|
return stmgr.GetMinerPeerID(ctx, a.StateManager, ts, m)
|
2019-09-16 20:11:17 +00:00
|
|
|
}
|
|
|
|
|
2019-11-28 17:44:49 +00:00
|
|
|
func (a *StateAPI) StateMinerElectionPeriodStart(ctx context.Context, actor address.Address, ts *types.TipSet) (uint64, error) {
|
|
|
|
return stmgr.GetMinerElectionPeriodStart(ctx, a.StateManager, ts, actor)
|
2019-09-18 00:08:49 +00:00
|
|
|
}
|
|
|
|
|
2019-10-16 07:07:16 +00:00
|
|
|
func (a *StateAPI) StateMinerSectorSize(ctx context.Context, actor address.Address, ts *types.TipSet) (uint64, error) {
|
|
|
|
return stmgr.GetMinerSectorSize(ctx, a.StateManager, ts, actor)
|
|
|
|
}
|
|
|
|
|
2020-01-30 00:50:58 +00:00
|
|
|
func (a *StateAPI) StateMinerFaults(ctx context.Context, addr address.Address, ts *types.TipSet) ([]uint64, error) {
|
|
|
|
return stmgr.GetMinerFaults(ctx, a.StateManager, ts, addr)
|
|
|
|
}
|
|
|
|
|
2019-09-25 23:32:33 +00:00
|
|
|
func (a *StateAPI) StatePledgeCollateral(ctx context.Context, ts *types.TipSet) (types.BigInt, error) {
|
|
|
|
param, err := actors.SerializeParams(&actors.PledgeCollateralParams{Size: types.NewInt(0)})
|
|
|
|
if err != nil {
|
|
|
|
return types.NewInt(0), err
|
|
|
|
}
|
|
|
|
|
|
|
|
ret, aerr := a.StateManager.Call(ctx, &types.Message{
|
2019-10-19 08:06:41 +00:00
|
|
|
From: actors.StoragePowerAddress,
|
|
|
|
To: actors.StoragePowerAddress,
|
2019-10-11 22:59:36 +00:00
|
|
|
Method: actors.SPAMethods.PledgeCollateralForSize,
|
2019-09-25 23:32:33 +00:00
|
|
|
|
|
|
|
Params: param,
|
|
|
|
}, ts)
|
|
|
|
if aerr != nil {
|
|
|
|
return types.NewInt(0), xerrors.Errorf("failed to get miner worker addr: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if ret.ExitCode != 0 {
|
|
|
|
return types.NewInt(0), xerrors.Errorf("failed to get miner worker addr (exit code %d)", ret.ExitCode)
|
|
|
|
}
|
|
|
|
|
|
|
|
return types.BigFromBytes(ret.Return), nil
|
|
|
|
}
|
|
|
|
|
2020-01-15 21:24:01 +00:00
|
|
|
func (a *StateAPI) StateCall(ctx context.Context, msg *types.Message, ts *types.TipSet) (*api.MethodCall, error) {
|
2019-09-09 20:03:10 +00:00
|
|
|
return a.StateManager.Call(ctx, msg, ts)
|
2019-09-06 06:26:02 +00:00
|
|
|
}
|
|
|
|
|
2019-09-19 20:25:18 +00:00
|
|
|
func (a *StateAPI) StateReplay(ctx context.Context, ts *types.TipSet, mc cid.Cid) (*api.ReplayResults, error) {
|
|
|
|
m, r, err := a.StateManager.Replay(ctx, ts, mc)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var errstr string
|
|
|
|
if r.ActorErr != nil {
|
|
|
|
errstr = r.ActorErr.Error()
|
|
|
|
}
|
|
|
|
|
|
|
|
return &api.ReplayResults{
|
|
|
|
Msg: m,
|
|
|
|
Receipt: &r.MessageReceipt,
|
|
|
|
Error: errstr,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2019-10-10 11:13:26 +00:00
|
|
|
func (a *StateAPI) stateForTs(ctx context.Context, ts *types.TipSet) (*state.StateTree, error) {
|
2019-09-06 06:26:02 +00:00
|
|
|
if ts == nil {
|
|
|
|
ts = a.Chain.GetHeaviestTipSet()
|
|
|
|
}
|
|
|
|
|
2019-10-10 11:13:26 +00:00
|
|
|
st, _, err := a.StateManager.TipSetState(ctx, ts)
|
2019-09-06 06:26:02 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
buf := bufbstore.NewBufferedBstore(a.Chain.Blockstore())
|
|
|
|
cst := hamt.CSTFromBstore(buf)
|
|
|
|
return state.LoadStateTree(cst, st)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *StateAPI) StateGetActor(ctx context.Context, actor address.Address, ts *types.TipSet) (*types.Actor, error) {
|
2019-10-10 11:13:26 +00:00
|
|
|
state, err := a.stateForTs(ctx, ts)
|
2019-09-06 06:26:02 +00:00
|
|
|
if err != nil {
|
2019-12-07 16:24:42 +00:00
|
|
|
return nil, xerrors.Errorf("computing tipset state failed: %w", err)
|
2019-09-06 06:26:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return state.GetActor(actor)
|
|
|
|
}
|
|
|
|
|
2019-11-15 16:39:43 +00:00
|
|
|
func (a *StateAPI) StateLookupID(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) {
|
|
|
|
state, err := a.stateForTs(ctx, ts)
|
|
|
|
if err != nil {
|
|
|
|
return address.Undef, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return state.LookupID(addr)
|
|
|
|
}
|
|
|
|
|
2019-09-06 06:26:02 +00:00
|
|
|
func (a *StateAPI) StateReadState(ctx context.Context, act *types.Actor, ts *types.TipSet) (*api.ActorState, error) {
|
2019-10-10 11:13:26 +00:00
|
|
|
state, err := a.stateForTs(ctx, ts)
|
2019-09-06 06:26:02 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
blk, err := state.Store.Blocks.GetBlock(ctx, act.Head)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
oif, err := vm.DumpActorState(act.Code, blk.RawData())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &api.ActorState{
|
|
|
|
Balance: act.Balance,
|
|
|
|
State: oif,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is on StateAPI because miner.Miner requires this, and MinerAPI requires miner.Miner
|
2019-11-21 22:21:45 +00:00
|
|
|
func (a *StateAPI) MinerCreateBlock(ctx context.Context, addr address.Address, parents *types.TipSet, ticket *types.Ticket, proof *types.EPostProof, msgs []*types.SignedMessage, height, ts uint64) (*types.BlockMsg, error) {
|
2019-11-19 15:53:00 +00:00
|
|
|
fblk, err := gen.MinerCreateBlock(ctx, a.StateManager, a.Wallet, addr, parents, ticket, proof, msgs, height, ts)
|
2019-09-06 06:26:02 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-09-18 02:50:03 +00:00
|
|
|
var out types.BlockMsg
|
2019-09-06 06:26:02 +00:00
|
|
|
out.Header = fblk.Header
|
|
|
|
for _, msg := range fblk.BlsMessages {
|
|
|
|
out.BlsMessages = append(out.BlsMessages, msg.Cid())
|
|
|
|
}
|
|
|
|
for _, msg := range fblk.SecpkMessages {
|
|
|
|
out.SecpkMessages = append(out.SecpkMessages, msg.Cid())
|
|
|
|
}
|
|
|
|
|
|
|
|
return &out, nil
|
|
|
|
}
|
2019-10-08 05:51:34 +00:00
|
|
|
|
|
|
|
func (a *StateAPI) StateWaitMsg(ctx context.Context, msg cid.Cid) (*api.MsgWait, error) {
|
|
|
|
// TODO: consider using event system for this, expose confidence
|
|
|
|
|
|
|
|
ts, recpt, err := a.StateManager.WaitForMessage(ctx, msg)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &api.MsgWait{
|
|
|
|
Receipt: *recpt,
|
|
|
|
TipSet: ts,
|
|
|
|
}, nil
|
|
|
|
}
|
2019-10-12 06:45:48 +00:00
|
|
|
|
2019-11-19 21:27:25 +00:00
|
|
|
func (a *StateAPI) StateGetReceipt(ctx context.Context, msg cid.Cid, ts *types.TipSet) (*types.MessageReceipt, error) {
|
|
|
|
return a.StateManager.GetReceipt(ctx, msg, ts)
|
|
|
|
}
|
|
|
|
|
2019-10-12 06:45:48 +00:00
|
|
|
func (a *StateAPI) StateListMiners(ctx context.Context, ts *types.TipSet) ([]address.Address, error) {
|
2019-12-17 06:51:41 +00:00
|
|
|
return stmgr.ListMinerActors(ctx, a.StateManager, ts)
|
2019-10-12 06:45:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (a *StateAPI) StateListActors(ctx context.Context, ts *types.TipSet) ([]address.Address, error) {
|
|
|
|
return a.StateManager.ListAllActors(ctx, ts)
|
|
|
|
}
|
2019-10-22 10:09:36 +00:00
|
|
|
|
|
|
|
func (a *StateAPI) StateMarketBalance(ctx context.Context, addr address.Address, ts *types.TipSet) (actors.StorageParticipantBalance, error) {
|
2019-10-23 17:39:14 +00:00
|
|
|
return a.StateManager.MarketBalance(ctx, addr, ts)
|
2019-10-22 10:09:36 +00:00
|
|
|
}
|
2019-10-22 19:29:05 +00:00
|
|
|
|
|
|
|
func (a *StateAPI) StateMarketParticipants(ctx context.Context, ts *types.TipSet) (map[string]actors.StorageParticipantBalance, error) {
|
|
|
|
out := map[string]actors.StorageParticipantBalance{}
|
|
|
|
|
|
|
|
var state actors.StorageMarketState
|
2019-10-23 12:59:57 +00:00
|
|
|
if _, err := a.StateManager.LoadActorState(ctx, actors.StorageMarketAddress, &state, ts); err != nil {
|
2019-10-22 19:29:05 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
cst := hamt.CSTFromBstore(a.StateManager.ChainStore().Blockstore())
|
|
|
|
nd, err := hamt.LoadNode(ctx, cst, state.Balances)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = nd.ForEach(ctx, func(k string, val interface{}) error {
|
|
|
|
cv := val.(*cbg.Deferred)
|
|
|
|
a, err := address.NewFromBytes([]byte(k))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
var b actors.StorageParticipantBalance
|
|
|
|
if err := b.UnmarshalCBOR(bytes.NewReader(cv.Raw)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
out[a.String()] = b
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *StateAPI) StateMarketDeals(ctx context.Context, ts *types.TipSet) (map[string]actors.OnChainDeal, error) {
|
|
|
|
out := map[string]actors.OnChainDeal{}
|
|
|
|
|
|
|
|
var state actors.StorageMarketState
|
2019-10-23 12:59:57 +00:00
|
|
|
if _, err := a.StateManager.LoadActorState(ctx, actors.StorageMarketAddress, &state, ts); err != nil {
|
2019-10-22 19:29:05 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
blks := amt.WrapBlockstore(a.StateManager.ChainStore().Blockstore())
|
|
|
|
da, err := amt.LoadAMT(blks, state.Deals)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := da.ForEach(func(i uint64, v *cbg.Deferred) error {
|
|
|
|
var d actors.OnChainDeal
|
|
|
|
if err := d.UnmarshalCBOR(bytes.NewReader(v.Raw)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
out[strconv.FormatInt(int64(i), 10)] = d
|
|
|
|
return nil
|
|
|
|
}); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return out, nil
|
|
|
|
}
|
2019-11-07 07:57:10 +00:00
|
|
|
|
|
|
|
func (a *StateAPI) StateMarketStorageDeal(ctx context.Context, dealId uint64, ts *types.TipSet) (*actors.OnChainDeal, error) {
|
2019-11-07 09:06:06 +00:00
|
|
|
return stmgr.GetStorageDeal(ctx, a.StateManager, dealId, ts)
|
2019-11-07 07:57:10 +00:00
|
|
|
}
|
2019-11-15 18:38:09 +00:00
|
|
|
|
|
|
|
func (a *StateAPI) StateChangedActors(ctx context.Context, old cid.Cid, new cid.Cid) (map[string]types.Actor, error) {
|
|
|
|
cst := hamt.CSTFromBstore(a.Chain.Blockstore())
|
|
|
|
|
|
|
|
nh, err := hamt.LoadNode(ctx, cst, new)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
oh, err := hamt.LoadNode(ctx, cst, old)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
out := map[string]types.Actor{}
|
|
|
|
|
|
|
|
err = nh.ForEach(ctx, func(k string, nval interface{}) error {
|
|
|
|
ncval := nval.(*cbg.Deferred)
|
|
|
|
var act types.Actor
|
|
|
|
|
|
|
|
var ocval cbg.Deferred
|
|
|
|
|
|
|
|
switch err := oh.Find(ctx, k, &ocval); err {
|
|
|
|
case nil:
|
|
|
|
if bytes.Equal(ocval.Raw, ncval.Raw) {
|
|
|
|
return nil // not changed
|
|
|
|
}
|
|
|
|
fallthrough
|
|
|
|
case hamt.ErrNotFound:
|
|
|
|
if err := act.UnmarshalCBOR(bytes.NewReader(ncval.Raw)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
addr, err := address.NewFromBytes([]byte(k))
|
|
|
|
if err != nil {
|
|
|
|
return xerrors.Errorf("address in state tree was not valid: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
out[addr.String()] = act
|
|
|
|
default:
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return out, nil
|
|
|
|
}
|
2019-12-11 23:31:59 +00:00
|
|
|
|
|
|
|
func (a *StateAPI) StateMinerSectorCount(ctx context.Context, addr address.Address, ts *types.TipSet) (api.MinerSectors, error) {
|
|
|
|
return stmgr.SectorSetSizes(ctx, a.StateManager, addr, ts)
|
|
|
|
}
|
2020-01-07 19:03:11 +00:00
|
|
|
|
|
|
|
func (a *StateAPI) StateListMessages(ctx context.Context, match *types.Message, ts *types.TipSet, toheight uint64) ([]cid.Cid, error) {
|
|
|
|
if ts == nil {
|
|
|
|
ts = a.Chain.GetHeaviestTipSet()
|
|
|
|
}
|
|
|
|
|
|
|
|
if match.To == address.Undef && match.From == address.Undef {
|
|
|
|
return nil, xerrors.Errorf("must specify at least To or From in message filter")
|
|
|
|
}
|
|
|
|
|
|
|
|
matchFunc := func(msg *types.Message) bool {
|
|
|
|
if match.From != address.Undef && match.From != msg.From {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if match.To != address.Undef && match.To != msg.To {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
var out []cid.Cid
|
|
|
|
for ts.Height() >= toheight {
|
|
|
|
msgs, err := a.Chain.MessagesForTipset(ts)
|
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("failed to get messages for tipset (%s): %w", ts.Key(), err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, msg := range msgs {
|
|
|
|
if matchFunc(msg.VMMessage()) {
|
|
|
|
out = append(out, msg.Cid())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ts.Height() == 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
next, err := a.Chain.LoadTipSet(ts.Parents())
|
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("loading next tipset: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
ts = next
|
|
|
|
}
|
|
|
|
|
|
|
|
return out, nil
|
|
|
|
}
|
2020-01-17 02:33:43 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
2020-01-30 01:23:16 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|