From aaad01105e7fca6fb9e9fa7804887d5ec9343d46 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Sat, 5 Sep 2020 20:29:26 -0400 Subject: [PATCH] Msig: Introduce an API & CLI to calculate amount that vests between 2 tipsets --- api/api_full.go | 3 ++ api/apistruct/struct.go | 5 ++++ cli/multisig.go | 63 +++++++++++++++++++++++++++++++++++++++++ node/impl/full/state.go | 42 +++++++++++++++++++++++++++ 4 files changed, 113 insertions(+) diff --git a/api/api_full.go b/api/api_full.go index 8ae857dfd..fb1810ad9 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -384,6 +384,9 @@ type FullNode interface { // MsigGetAvailableBalance returns the portion of a multisig's balance that can be withdrawn or spent MsigGetAvailableBalance(context.Context, address.Address, types.TipSetKey) (types.BigInt, error) + // MsigGetVested returns the amount of FIL that vested in a multisig in a certain period. + // It takes the following params: , , + MsigGetVested(context.Context, address.Address, types.TipSetKey, types.TipSetKey) (types.BigInt, error) // MsigCreate creates a multisig wallet // It takes the following params: , , //, , diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index e2444f16b..de0ddd31b 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -197,6 +197,7 @@ type FullNodeStruct struct { StateCirculatingSupply func(context.Context, types.TipSetKey) (api.CirculatingSupply, error) `perm:"read"` MsigGetAvailableBalance func(context.Context, address.Address, types.TipSetKey) (types.BigInt, error) `perm:"read"` + MsigGetVested func(context.Context, address.Address, types.TipSetKey, types.TipSetKey) (types.BigInt, error) `perm:"read"` MsigCreate func(context.Context, uint64, []address.Address, abi.ChainEpoch, types.BigInt, address.Address, types.BigInt) (cid.Cid, error) `perm:"sign"` MsigPropose func(context.Context, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error) `perm:"sign"` MsigApprove func(context.Context, address.Address, uint64, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error) `perm:"sign"` @@ -866,6 +867,10 @@ func (c *FullNodeStruct) MsigGetAvailableBalance(ctx context.Context, a address. return c.Internal.MsigGetAvailableBalance(ctx, a, tsk) } +func (c *FullNodeStruct) MsigGetVested(ctx context.Context, a address.Address, sTsk types.TipSetKey, eTsk types.TipSetKey) (types.BigInt, error) { + return c.Internal.MsigGetVested(ctx, a, sTsk, eTsk) +} + func (c *FullNodeStruct) MsigCreate(ctx context.Context, req uint64, addrs []address.Address, duration abi.ChainEpoch, val types.BigInt, src address.Address, gp types.BigInt) (cid.Cid, error) { return c.Internal.MsigCreate(ctx, req, addrs, duration, val, src, gp) } diff --git a/cli/multisig.go b/cli/multisig.go index 57f6c2c03..70780fd29 100644 --- a/cli/multisig.go +++ b/cli/multisig.go @@ -40,6 +40,7 @@ var multisigCmd = &cli.Command{ msigSwapProposeCmd, msigSwapApproveCmd, msigSwapCancelCmd, + msigVestedCmd, }, } @@ -736,3 +737,65 @@ var msigSwapCancelCmd = &cli.Command{ return nil }, } + +var msigVestedCmd = &cli.Command{ + Name: "vested", + Usage: "Gets the amount vested in an msig between two epochs", + ArgsUsage: "[multisigAddress]", + Flags: []cli.Flag{ + &cli.Int64Flag{ + Name: "start-epoch", + Usage: "start epoch to measure vesting from", + Value: 0, + }, + &cli.Int64Flag{ + Name: "end-epoch", + Usage: "end epoch to stop measure vesting at", + Value: -1, + }, + }, + Action: func(cctx *cli.Context) error { + if cctx.Args().Len() != 1 { + return ShowHelp(cctx, fmt.Errorf("must pass multisig address")) + } + + api, closer, err := GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer closer() + ctx := ReqContext(cctx) + + msig, err := address.NewFromString(cctx.Args().Get(0)) + if err != nil { + return err + } + + start, err := api.ChainGetTipSetByHeight(ctx, abi.ChainEpoch(cctx.Int64("start-epoch")), types.EmptyTSK) + if err != nil { + return err + } + + var end *types.TipSet + if cctx.Int64("end-epoch") < 0 { + end, err = LoadTipSet(ctx, cctx, api) + if err != nil { + return err + } + } else { + end, err = api.ChainGetTipSetByHeight(ctx, abi.ChainEpoch(cctx.Int64("end-epoch")), types.EmptyTSK) + if err != nil { + return err + } + } + + ret, err := api.MsigGetVested(ctx, msig, start.Key(), end.Key()) + if err != nil { + return err + } + + fmt.Printf("Vested: %s between %d and %d\n", types.FIL(ret), start.Height(), end.Height()) + + return nil + }, +} diff --git a/node/impl/full/state.go b/node/impl/full/state.go index 36721a93d..f2b0df0ec 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -886,6 +886,48 @@ func (a *StateAPI) MsigGetAvailableBalance(ctx context.Context, addr address.Add return types.BigSub(act.Balance, minBalance), nil } +func (a *StateAPI) MsigGetVested(ctx context.Context, addr address.Address, start types.TipSetKey, end types.TipSetKey) (types.BigInt, error) { + startTs, err := a.Chain.GetTipSetFromKey(start) + if err != nil { + return types.EmptyInt, xerrors.Errorf("loading start tipset %s: %w", start, err) + } + + endTs, err := a.Chain.GetTipSetFromKey(end) + if err != nil { + return types.EmptyInt, xerrors.Errorf("loading end tipset %s: %w", end, err) + } + + if startTs.Height() > endTs.Height() { + return types.EmptyInt, xerrors.Errorf("start tipset %d is after end tipset %d", startTs.Height(), endTs.Height()) + } else if startTs.Height() == endTs.Height() { + return big.Zero(), nil + } + + var mst samsig.State + act, err := a.StateManager.LoadActorState(ctx, addr, &mst, endTs) + if err != nil { + return types.EmptyInt, xerrors.Errorf("failed to load multisig actor state at end epoch: %w", err) + } + + if act.Code != builtin.MultisigActorCodeID { + return types.EmptyInt, fmt.Errorf("given actor was not a multisig") + } + + if mst.UnlockDuration == 0 || + mst.InitialBalance.IsZero() || + mst.StartEpoch+mst.UnlockDuration <= startTs.Height() || + mst.StartEpoch >= endTs.Height() { + return big.Zero(), nil + } + + startLk := mst.InitialBalance + if startTs.Height() > mst.StartEpoch { + startLk = mst.AmountLocked(startTs.Height() - mst.StartEpoch) + } + + return big.Sub(startLk, mst.AmountLocked(endTs.Height()-mst.StartEpoch)), nil +} + var initialPledgeNum = types.NewInt(110) var initialPledgeDen = types.NewInt(100)