Msig: Introduce an API & CLI to calculate amount that vests between 2 tipsets

This commit is contained in:
Aayush Rajasekaran 2020-09-05 20:29:26 -04:00
parent 6bdd433570
commit aaad01105e
4 changed files with 113 additions and 0 deletions

View File

@ -384,6 +384,9 @@ type FullNode interface {
// MsigGetAvailableBalance returns the portion of a multisig's balance that can be withdrawn or spent // 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) 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: <multisig address>, <start epoch>, <end epoch>
MsigGetVested(context.Context, address.Address, types.TipSetKey, types.TipSetKey) (types.BigInt, error)
// MsigCreate creates a multisig wallet // MsigCreate creates a multisig wallet
// It takes the following params: <required number of senders>, <approving addresses>, <unlock duration> // It takes the following params: <required number of senders>, <approving addresses>, <unlock duration>
//<initial balance>, <sender address of the create msg>, <gas price> //<initial balance>, <sender address of the create msg>, <gas price>

View File

@ -197,6 +197,7 @@ type FullNodeStruct struct {
StateCirculatingSupply func(context.Context, types.TipSetKey) (api.CirculatingSupply, error) `perm:"read"` StateCirculatingSupply func(context.Context, types.TipSetKey) (api.CirculatingSupply, error) `perm:"read"`
MsigGetAvailableBalance func(context.Context, address.Address, types.TipSetKey) (types.BigInt, 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"` 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"` 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"` 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) 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) { 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) return c.Internal.MsigCreate(ctx, req, addrs, duration, val, src, gp)
} }

View File

@ -40,6 +40,7 @@ var multisigCmd = &cli.Command{
msigSwapProposeCmd, msigSwapProposeCmd,
msigSwapApproveCmd, msigSwapApproveCmd,
msigSwapCancelCmd, msigSwapCancelCmd,
msigVestedCmd,
}, },
} }
@ -736,3 +737,65 @@ var msigSwapCancelCmd = &cli.Command{
return nil 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
},
}

View File

@ -886,6 +886,48 @@ func (a *StateAPI) MsigGetAvailableBalance(ctx context.Context, addr address.Add
return types.BigSub(act.Balance, minBalance), nil 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 initialPledgeNum = types.NewInt(110)
var initialPledgeDen = types.NewInt(100) var initialPledgeDen = types.NewInt(100)