lotus/cli/multisig.go

1824 lines
40 KiB
Go
Raw Normal View History

2020-01-21 19:02:51 +00:00
package cli
import (
"bytes"
"encoding/hex"
"encoding/json"
2020-01-21 19:02:51 +00:00
"fmt"
"reflect"
2020-02-04 03:37:55 +00:00
"sort"
2020-01-21 19:02:51 +00:00
"strconv"
"text/tabwriter"
"github.com/ipfs/go-cid"
2020-02-04 22:19:05 +00:00
cbor "github.com/ipfs/go-ipld-cbor"
2020-06-05 22:59:01 +00:00
"github.com/urfave/cli/v2"
2022-06-14 15:00:51 +00:00
cbg "github.com/whyrusleeping/cbor-gen"
2020-02-04 03:37:55 +00:00
"golang.org/x/xerrors"
2020-02-06 19:49:21 +00:00
2022-06-14 15:00:51 +00:00
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
2020-10-08 01:09:33 +00:00
init2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/init"
msig2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/multisig"
"github.com/filecoin-project/lotus/blockstore"
2020-06-04 02:34:57 +00:00
"github.com/filecoin-project/lotus/build"
2021-09-02 16:58:34 +00:00
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/actors/adt"
2021-09-02 16:58:34 +00:00
"github.com/filecoin-project/lotus/chain/actors/builtin"
"github.com/filecoin-project/lotus/chain/actors/builtin/multisig"
2021-09-02 16:58:34 +00:00
"github.com/filecoin-project/lotus/chain/consensus/filcns"
"github.com/filecoin-project/lotus/chain/types"
2020-01-21 19:02:51 +00:00
)
var multisigCmd = &cli.Command{
Name: "msig",
Usage: "Interact with a multisig wallet",
2020-10-14 17:56:51 +00:00
Flags: []cli.Flag{
&cli.IntFlag{
2020-10-15 05:55:23 +00:00
Name: "confidence",
2020-10-14 17:56:51 +00:00
Usage: "number of block confirmations to wait for",
Value: int(build.MessageConfidence),
},
},
2020-01-21 19:02:51 +00:00
Subcommands: []*cli.Command{
msigCreateCmd,
msigInspectCmd,
msigProposeCmd,
msigRemoveProposeCmd,
2020-01-21 19:02:51 +00:00
msigApproveCmd,
msigCancelCmd,
msigAddProposeCmd,
msigAddApproveCmd,
msigAddCancelCmd,
2020-07-16 00:55:27 +00:00
msigSwapProposeCmd,
msigSwapApproveCmd,
msigSwapCancelCmd,
msigLockProposeCmd,
msigLockApproveCmd,
msigLockCancelCmd,
msigVestedCmd,
msigProposeThresholdCmd,
2020-01-21 19:02:51 +00:00
},
}
var msigCreateCmd = &cli.Command{
2020-03-06 19:01:28 +00:00
Name: "create",
Usage: "Create a new multisig wallet",
ArgsUsage: "[address1 address2 ...]",
2020-01-21 19:02:51 +00:00
Flags: []cli.Flag{
2020-02-04 03:37:55 +00:00
&cli.Int64Flag{
2020-07-13 23:50:18 +00:00
Name: "required",
Usage: "number of required approvals (uses number of signers provided if omitted)",
2020-01-21 19:02:51 +00:00
},
&cli.StringFlag{
Name: "value",
Usage: "initial funds to give to multisig",
Value: "0",
},
&cli.StringFlag{
Name: "duration",
Usage: "length of the period over which funds unlock",
Value: "0",
},
&cli.StringFlag{
Name: "from",
Usage: "account to send the create message from",
},
2020-01-21 19:02:51 +00:00
},
Action: func(cctx *cli.Context) error {
2022-09-14 18:33:29 +00:00
if cctx.NArg() < 1 {
return IncorrectNumArgs(cctx)
}
srv, err := GetFullNodeServices(cctx)
2020-01-21 19:02:51 +00:00
if err != nil {
return err
}
defer srv.Close() //nolint:errcheck
api := srv.FullNodeAPI()
2020-01-21 19:02:51 +00:00
ctx := ReqContext(cctx)
var addrs []address.Address
for _, a := range cctx.Args().Slice() {
addr, err := address.NewFromString(a)
if err != nil {
return err
}
addrs = append(addrs, addr)
}
// get the address we're going to use to create the multisig (can be one of the above, as long as they have funds)
var sendAddr address.Address
if send := cctx.String("from"); send == "" {
defaddr, err := api.WalletDefaultAddress(ctx)
if err != nil {
return err
}
sendAddr = defaddr
} else {
addr, err := address.NewFromString(send)
if err != nil {
return err
}
sendAddr = addr
2020-01-21 19:02:51 +00:00
}
val := cctx.String("value")
filval, err := types.ParseFIL(val)
if err != nil {
return err
}
intVal := types.BigInt(filval)
required := cctx.Uint64("required")
2020-01-21 19:02:51 +00:00
if required == 0 {
required = uint64(len(addrs))
2020-01-21 19:02:51 +00:00
}
d := abi.ChainEpoch(cctx.Uint64("duration"))
gp := types.NewInt(1)
2020-01-21 19:02:51 +00:00
proto, err := api.MsigCreate(ctx, required, addrs, d, intVal, sendAddr, gp)
if err != nil {
return err
}
sm, err := InteractiveSend(ctx, cctx, srv, proto)
2020-01-21 19:02:51 +00:00
if err != nil {
return err
}
msgCid := sm.Cid()
fmt.Println("sent create in message: ", msgCid)
fmt.Println("waiting for confirmation..")
2020-01-21 19:02:51 +00:00
// wait for it to get mined into a block
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
2020-01-21 19:02:51 +00:00
if err != nil {
return err
}
// check it executed successfully
if wait.Receipt.ExitCode.IsError() {
fmt.Fprintln(cctx.App.Writer, "actor creation failed!")
2020-01-21 19:02:51 +00:00
return err
}
// get address of newly created miner
2020-10-08 01:09:33 +00:00
var execreturn init2.ExecReturn
if err := execreturn.UnmarshalCBOR(bytes.NewReader(wait.Receipt.Return)); err != nil {
2020-01-21 19:02:51 +00:00
return err
}
fmt.Fprintln(cctx.App.Writer, "Created new multisig: ", execreturn.IDAddress, execreturn.RobustAddress)
2020-04-17 16:27:11 +00:00
// TODO: maybe register this somewhere
2020-01-21 19:02:51 +00:00
return nil
},
}
var msigInspectCmd = &cli.Command{
2020-03-06 19:01:28 +00:00
Name: "inspect",
Usage: "Inspect a multisig wallet",
ArgsUsage: "[address]",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "vesting",
Usage: "Include vesting details",
},
&cli.BoolFlag{
Name: "decode-params",
Usage: "Decode parameters of transaction proposals",
},
},
2020-01-21 19:02:51 +00:00
Action: func(cctx *cli.Context) error {
if !cctx.Args().Present() {
return ShowHelp(cctx, fmt.Errorf("must specify address of multisig to inspect"))
}
2020-01-21 19:02:51 +00:00
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := ReqContext(cctx)
store := adt.WrapStore(ctx, cbor.NewCborStore(blockstore.NewAPIBlockstore(api)))
2020-01-21 19:02:51 +00:00
maddr, err := address.NewFromString(cctx.Args().First())
if err != nil {
return err
}
head, err := api.ChainHead(ctx)
2020-01-21 19:02:51 +00:00
if err != nil {
return err
}
act, err := api.StateGetActor(ctx, maddr, head.Key())
2020-01-21 19:02:51 +00:00
if err != nil {
return err
}
ownId, err := api.StateLookupID(ctx, maddr, types.EmptyTSK)
if err != nil {
return err
}
mstate, err := multisig.Load(store, act)
if err != nil {
return err
}
locked, err := mstate.LockedBalance(head.Height())
if err != nil {
2020-01-21 19:02:51 +00:00
return err
}
fmt.Fprintf(cctx.App.Writer, "Balance: %s\n", types.FIL(act.Balance))
fmt.Fprintf(cctx.App.Writer, "Spendable: %s\n", types.FIL(types.BigSub(act.Balance, locked)))
if cctx.Bool("vesting") {
ib, err := mstate.InitialBalance()
if err != nil {
return err
}
fmt.Fprintf(cctx.App.Writer, "InitialBalance: %s\n", types.FIL(ib))
se, err := mstate.StartEpoch()
if err != nil {
return err
}
fmt.Fprintf(cctx.App.Writer, "StartEpoch: %d\n", se)
ud, err := mstate.UnlockDuration()
if err != nil {
return err
}
fmt.Fprintf(cctx.App.Writer, "UnlockDuration: %d\n", ud)
}
signers, err := mstate.Signers()
if err != nil {
return err
}
threshold, err := mstate.Threshold()
if err != nil {
return err
}
fmt.Fprintf(cctx.App.Writer, "Threshold: %d / %d\n", threshold, len(signers))
fmt.Fprintln(cctx.App.Writer, "Signers:")
signerTable := tabwriter.NewWriter(cctx.App.Writer, 8, 4, 2, ' ', 0)
fmt.Fprintf(signerTable, "ID\tAddress\n")
for _, s := range signers {
signerActor, err := api.StateAccountKey(ctx, s, types.EmptyTSK)
if err != nil {
fmt.Fprintf(signerTable, "%s\t%s\n", s, "N/A")
} else {
fmt.Fprintf(signerTable, "%s\t%s\n", s, signerActor)
}
}
if err := signerTable.Flush(); err != nil {
return xerrors.Errorf("flushing output: %+v", err)
2020-01-21 19:02:51 +00:00
}
2020-02-04 03:37:55 +00:00
pending := make(map[int64]multisig.Transaction)
if err := mstate.ForEachPendingTxn(func(id int64, txn multisig.Transaction) error {
pending[id] = txn
return nil
}); err != nil {
return xerrors.Errorf("reading pending transactions: %w", err)
2020-02-04 03:37:55 +00:00
}
decParams := cctx.Bool("decode-params")
fmt.Fprintln(cctx.App.Writer, "Transactions: ", len(pending))
2020-02-04 03:37:55 +00:00
if len(pending) > 0 {
var txids []int64
for txid := range pending {
txids = append(txids, txid)
}
sort.Slice(txids, func(i, j int) bool {
return txids[i] < txids[j]
})
w := tabwriter.NewWriter(cctx.App.Writer, 8, 4, 2, ' ', 0)
fmt.Fprintf(w, "ID\tState\tApprovals\tTo\tValue\tMethod\tParams\n")
2020-02-04 03:37:55 +00:00
for _, txid := range txids {
tx := pending[txid]
target := tx.To.String()
if tx.To == ownId {
target += " (self)"
}
targAct, err := api.StateGetActor(ctx, tx.To, types.EmptyTSK)
paramStr := fmt.Sprintf("%x", tx.Params)
if err != nil {
if tx.Method == 0 {
fmt.Fprintf(w, "%d\t%s\t%d\t%s\t%s\t%s(%d)\t%s\n", txid, "pending", len(tx.Approved), target, types.FIL(tx.Value), "Send", tx.Method, paramStr)
} else {
fmt.Fprintf(w, "%d\t%s\t%d\t%s\t%s\t%s(%d)\t%s\n", txid, "pending", len(tx.Approved), target, types.FIL(tx.Value), "new account, unknown method", tx.Method, paramStr)
}
} else {
2021-09-02 16:58:34 +00:00
method := filcns.NewActorRegistry().Methods[targAct.Code][tx.Method] // TODO: use remote map
if decParams && tx.Method != 0 {
ptyp := reflect.New(method.Params.Elem()).Interface().(cbg.CBORUnmarshaler)
if err := ptyp.UnmarshalCBOR(bytes.NewReader(tx.Params)); err != nil {
return xerrors.Errorf("failed to decode parameters of transaction %d: %w", txid, err)
}
b, err := json.Marshal(ptyp)
if err != nil {
return xerrors.Errorf("could not json marshal parameter type: %w", err)
}
paramStr = string(b)
}
build: release: v1.18.0 (#9652) * build: Bump version to v1.17.3-dev * build: set version to v1.18.0-dev * chore: actors: Allow builtin-actors to return a map of methods (#9342) * Allow builtin-actors to return a map of methods * go mod * Fix tests * Fix tests, check carefully please * Delete lotus-pond (#9352) * feat: add StateNetworkVersion to mpool API * chore: refactor: rename NewestNetworkVersion * feat: actors: Integrate datacap actor into lotus (#9348) * Integrate datacap actor * Implement datacap actor in chain/builtin * feat: support typed errors over RPC * chore: deps: update to go-jsonrpc 0.1.8 * remove duplicate import * fix: itest: check for closed connection * chore: refactor: move retry test to API * address magik supernit * Add ability to only have single partition per msg for partitions with recovery sectors * doc gen * Address comments * Return beneficiary info from miner state Info() * Update builtin-actors to dev/20220922-v9 which includes FIP-0045 changes in progress * Integrate verifreg changes to lotus * Setup datacap actor * Update builtin-actors to dev/20220922-v9-1 * Update datacap actor to query datacap instead of verifreg * update gst * update markets * update actors with hamt fix * update gst * Update datacap to parse tokens * Update bundles * datacap and verifreg actors use ID addresses without protocol byte * update builtin-actors to rc1 * update go-fil-markets * Update bundles to rc2 * Integrate the v9 migration * Add api for getting allocation * Add upgrade epoch for butterfly * Tweak PreSeal struct to be infra-friendly * docsgen * More tweaking of PreSeal for genesis * review fixes * Use fake cid for test * add butterfly artifacts for oct 5 upgrade * check datacaps for v8 verifreg match v9 datacap actor * Remove print statements * Update to go-state-types master * Update to go-state-types v0.9.0-rc1 * review fixes * use go-fil-markets v1.24.0-v17 * Add accessors for allocations and claims maps * fix: missing permissions tag * butterfly * update butterfly artifacts * sealing pipeline: Prepare deal assigning logic for FIP-45 * sealing pipeline: Get allocationId with StateApi * use NoAllocationID instead of nil AllocationId * address review * Add datacap actor to registry.go * Add cli for listing allocations and removing expired allocations * Update to go-state-types master * deps: upgrade go-merkledag to 0.8.0 * shark params * Update cli/filplus.go Co-authored-by: Aayush Rajasekaran <arajasek94@gmail.com> * revert change to verifreg util * docsgen-cli * miss the stuff * Update FFI * Update go-state-types to v0.9.0 * Update builtin-actors to v9.0.0 * add calib upgrade epcoh * update the upgrade envvar * kill shark * Remove fvm splash banner from nv17 upgrade * check invariance for pending deals and allocations * check pending verified deal proposal migrated to allocation * Add check for unsealed CID in precommit sectors * Fix counting of allocations in nv17 migration test * make gen * pass state trees as pointers * Add assertion that migrations with & without cache are the same * compare allocation to verified deal proposal * Fix miner state precommit info * fix migration test tool * add changelog * Update to go-state-types v0.9.1 * Integrate builtin-actors v9.0.1 * chore: ver: bump version for rc3 (#9512) * Bump version to 1.18.0-rc3 * Update CHANGELOG.md * Update CHANGELOG.md Co-authored-by: Aayush Rajasekaran <arajasek94@gmail.com> * Update CHANGELOG.md Co-authored-by: Aayush Rajasekaran <arajasek94@gmail.com> Co-authored-by: Jiaying Wang <42981373+jennijuju@users.noreply.github.com> Co-authored-by: Aayush Rajasekaran <arajasek94@gmail.com> * Migration: Use autobatch bs * Fix autobatch Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai> * Invoker: Use MethodMeta from go-state-types * Add a second premigration for nv17 * Add more shed tools for migration checking * address review * Lotus release v1.18.0-rc4 * fix: ci: fix app-image build on ci (#9527) * Remove old go version first * Add GO_VERSION file * Use GO_VERSION to set / verify go version * mv GO_VERSION GO_VERSION_MIN * Use GO_VERSION_MIN in Makefile check Co-authored-by: Ian Davis <jungziege@gmail.com> * Update to latest go-state-types for migration fixes * go mod tidy * fix: use api.ErrActorNotFound instead of types.ErrActorNotFound * fix: add fields to ForkUpgradeParams * docs: update actors_version_checklist.md * chore: fix lint * update to go state type v0.9.6 with market migration fix (#9545) * update go-state-types to v-0.9.7 * Add invariant checks to migration * fix invariant check: number of entries in datacap actor should include verifreg * Invariant checks: Only include not-activated deals * test: nv17 migration * Address review * add lotus-shed invariance method * Migration cli takes a stateroot cid and a height * make gen * Update to builtin-actors v9.0.2 * Failing test that shows that notaries can remove datacap from the verifreg actor * Test that should pass when the problem is solved * make gen * Review fixes * statemanager call function will return call information even if call errors * update go-state-types * update builtin-actors * bubble up errors properly from ApplyImplicitMessage * bump to rc5 * set new upgrade heights for calibnet * set new upgrade height for butterfly * tweak calibnet upgrade schedule * clarify changelog note about calibnet * butterfly * update calibnet artifacts * Allow setting local bundles for Debug FVM for av 9+ * fix: autobatch: remove potential deadlock when a block is missing Check the _underlying_ blockstore instead of recursing. Also, drop the lock before we do that. * fix imports * build: set shark mainnet epoch (#9640) * chore: build: Lotus release v1.18.0 (#9641) * Lotus release v1.18.0 * add changelog * address review * changelog improvement Co-authored-by: Jennifer Wang <jiayingw703@gmail.com> Co-authored-by: Jiaying Wang <42981373+jennijuju@users.noreply.github.com> Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai> Co-authored-by: Łukasz Magiera <magik6k@gmail.com> Co-authored-by: Łukasz Magiera <magik6k@users.noreply.github.com> Co-authored-by: Aayush <arajasek94@gmail.com> Co-authored-by: Geoff Stuart <geoff.vball@gmail.com> Co-authored-by: Shrenuj Bansal <shrenuj.bansal@protocol.ai> Co-authored-by: simlecode <69969590+simlecode@users.noreply.github.com> Co-authored-by: Rod Vagg <rod@vagg.org> Co-authored-by: Jakub Sztandera <kubuxu@protocol.ai> Co-authored-by: Ian Davis <jungziege@gmail.com> Co-authored-by: zenground0 <ZenGround0@users.noreply.github.com> Co-authored-by: Steven Allen <steven@stebalien.com>
2022-11-16 01:57:23 +00:00
fmt.Fprintf(w, "%d\t%s\t%d\t%s\t%s\t%s(%d)\t%s\n", txid, "pending", len(tx.Approved), target, types.FIL(tx.Value), method.Name, tx.Method, paramStr)
}
2020-01-21 19:02:51 +00:00
}
if err := w.Flush(); err != nil {
return xerrors.Errorf("flushing output: %+v", err)
}
2020-01-21 19:02:51 +00:00
}
return nil
},
}
var msigProposeCmd = &cli.Command{
2020-03-06 19:01:28 +00:00
Name: "propose",
Usage: "Propose a multisig transaction",
ArgsUsage: "[multisigAddress destinationAddress value <methodId methodParams> (optional)]",
2020-04-24 22:00:31 +00:00
Flags: []cli.Flag{
&cli.StringFlag{
2020-07-16 00:55:27 +00:00
Name: "from",
2020-04-24 22:00:31 +00:00
Usage: "account to send the propose message from",
},
},
2020-01-21 19:02:51 +00:00
Action: func(cctx *cli.Context) error {
2022-09-14 18:33:29 +00:00
if cctx.NArg() < 3 {
return ShowHelp(cctx, fmt.Errorf("must pass at least multisig address, destination, and value"))
}
2022-09-14 18:33:29 +00:00
if cctx.NArg() > 3 && cctx.NArg() != 5 {
return ShowHelp(cctx, fmt.Errorf("must either pass three or five arguments"))
}
srv, err := GetFullNodeServices(cctx)
2020-01-21 19:02:51 +00:00
if err != nil {
return err
}
defer srv.Close() //nolint:errcheck
api := srv.FullNodeAPI()
2020-01-21 19:02:51 +00:00
ctx := ReqContext(cctx)
msig, err := address.NewFromString(cctx.Args().Get(0))
if err != nil {
return err
}
dest, err := address.NewFromString(cctx.Args().Get(1))
if err != nil {
return err
}
value, err := types.ParseFIL(cctx.Args().Get(2))
if err != nil {
return err
}
var method uint64
var params []byte
2022-09-14 18:33:29 +00:00
if cctx.NArg() == 5 {
2020-01-21 19:02:51 +00:00
m, err := strconv.ParseUint(cctx.Args().Get(3), 10, 64)
if err != nil {
return err
}
method = m
p, err := hex.DecodeString(cctx.Args().Get(4))
if err != nil {
return err
}
params = p
}
var from address.Address
2020-07-16 00:55:27 +00:00
if cctx.IsSet("from") {
f, err := address.NewFromString(cctx.String("from"))
2020-01-21 19:02:51 +00:00
if err != nil {
return err
}
from = f
} else {
defaddr, err := api.WalletDefaultAddress(ctx)
if err != nil {
return err
}
from = defaddr
}
act, err := api.StateGetActor(ctx, msig, types.EmptyTSK)
if err != nil {
return fmt.Errorf("failed to look up multisig %s: %w", msig, err)
}
2020-09-29 04:24:38 +00:00
if !builtin.IsMultisigActor(act.Code) {
return fmt.Errorf("actor %s is not a multisig actor", msig)
}
proto, err := api.MsigPropose(ctx, msig, dest, types.BigInt(value), from, method, params)
if err != nil {
return err
}
sm, err := InteractiveSend(ctx, cctx, srv, proto)
2020-01-21 19:02:51 +00:00
if err != nil {
return err
}
msgCid := sm.Cid()
fmt.Println("sent proposal in message: ", msgCid)
2020-01-21 19:02:51 +00:00
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
2020-01-21 19:02:51 +00:00
if err != nil {
return err
}
if wait.Receipt.ExitCode.IsError() {
2020-01-21 19:02:51 +00:00
return fmt.Errorf("proposal returned exit %d", wait.Receipt.ExitCode)
}
2020-10-08 01:09:33 +00:00
var retval msig2.ProposeReturn
2020-07-21 00:17:53 +00:00
if err := retval.UnmarshalCBOR(bytes.NewReader(wait.Receipt.Return)); err != nil {
return fmt.Errorf("failed to unmarshal propose return value: %w", err)
2020-01-21 19:02:51 +00:00
}
2020-07-21 00:17:53 +00:00
fmt.Printf("Transaction ID: %d\n", retval.TxnID)
if retval.Applied {
fmt.Printf("Transaction was executed during propose\n")
fmt.Printf("Exit Code: %d\n", retval.Code)
fmt.Printf("Return Value: %x\n", retval.Ret)
}
2020-01-21 19:02:51 +00:00
return nil
},
}
var msigApproveCmd = &cli.Command{
2020-03-06 19:01:28 +00:00
Name: "approve",
Usage: "Approve a multisig message",
ArgsUsage: "<multisigAddress messageId> [proposerAddress destination value [methodId methodParams]]",
Flags: []cli.Flag{
&cli.StringFlag{
2020-07-16 00:55:27 +00:00
Name: "from",
Usage: "account to send the approve message from",
},
},
2020-01-21 19:02:51 +00:00
Action: func(cctx *cli.Context) error {
2022-09-14 18:33:29 +00:00
if cctx.NArg() < 2 {
2020-10-14 17:56:51 +00:00
return ShowHelp(cctx, fmt.Errorf("must pass at least multisig address and message ID"))
}
2022-09-14 18:33:29 +00:00
if cctx.NArg() > 2 && cctx.NArg() < 5 {
2021-01-13 08:03:19 +00:00
return ShowHelp(cctx, fmt.Errorf("usage: msig approve <msig addr> <message ID> <proposer address> <desination> <value>"))
}
2022-09-14 18:33:29 +00:00
if cctx.NArg() > 5 && cctx.NArg() != 7 {
2021-01-13 08:03:19 +00:00
return ShowHelp(cctx, fmt.Errorf("usage: msig approve <msig addr> <message ID> <proposer address> <desination> <value> [ <method> <params> ]"))
2020-10-14 17:56:51 +00:00
}
srv, err := GetFullNodeServices(cctx)
2020-01-21 19:02:51 +00:00
if err != nil {
return err
}
defer srv.Close() //nolint:errcheck
api := srv.FullNodeAPI()
2020-01-21 19:02:51 +00:00
ctx := ReqContext(cctx)
msig, err := address.NewFromString(cctx.Args().Get(0))
if err != nil {
return err
}
txid, err := strconv.ParseUint(cctx.Args().Get(1), 10, 64)
if err != nil {
return err
}
2020-10-14 17:56:51 +00:00
var from address.Address
if cctx.IsSet("from") {
f, err := address.NewFromString(cctx.String("from"))
if err != nil {
return err
}
2020-10-14 17:56:51 +00:00
from = f
} else {
defaddr, err := api.WalletDefaultAddress(ctx)
if err != nil {
return err
}
from = defaddr
}
2020-10-14 17:56:51 +00:00
var msgCid cid.Cid
2022-09-14 18:33:29 +00:00
if cctx.NArg() == 2 {
proto, err := api.MsigApprove(ctx, msig, txid, from)
if err != nil {
return err
}
sm, err := InteractiveSend(ctx, cctx, srv, proto)
if err != nil {
return err
}
msgCid = sm.Cid()
2020-10-14 17:56:51 +00:00
} else {
proposer, err := address.NewFromString(cctx.Args().Get(2))
if err != nil {
return err
}
2020-10-14 17:56:51 +00:00
if proposer.Protocol() != address.ID {
proposer, err = api.StateLookupID(ctx, proposer, types.EmptyTSK)
if err != nil {
return err
}
}
dest, err := address.NewFromString(cctx.Args().Get(3))
2020-01-21 19:02:51 +00:00
if err != nil {
return err
}
2020-10-14 17:56:51 +00:00
value, err := types.ParseFIL(cctx.Args().Get(4))
2020-01-21 19:02:51 +00:00
if err != nil {
return err
}
2020-10-14 17:56:51 +00:00
var method uint64
var params []byte
2022-09-14 18:33:29 +00:00
if cctx.NArg() == 7 {
2020-10-14 17:56:51 +00:00
m, err := strconv.ParseUint(cctx.Args().Get(5), 10, 64)
if err != nil {
return err
}
method = m
p, err := hex.DecodeString(cctx.Args().Get(6))
if err != nil {
return err
}
params = p
}
proto, err := api.MsigApproveTxnHash(ctx, msig, txid, proposer, dest, types.BigInt(value), from, method, params)
2020-10-14 17:56:51 +00:00
if err != nil {
return err
}
sm, err := InteractiveSend(ctx, cctx, srv, proto)
if err != nil {
return err
}
msgCid = sm.Cid()
2020-01-21 19:02:51 +00:00
}
fmt.Println("sent approval in message: ", msgCid)
2020-01-21 19:02:51 +00:00
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
2020-01-21 19:02:51 +00:00
if err != nil {
return err
}
if wait.Receipt.ExitCode.IsError() {
2020-01-21 19:02:51 +00:00
return fmt.Errorf("approve returned exit %d", wait.Receipt.ExitCode)
}
return nil
},
}
2020-07-16 00:55:27 +00:00
var msigCancelCmd = &cli.Command{
Name: "cancel",
Usage: "Cancel a multisig message",
ArgsUsage: "<multisigAddress messageId> [destination value [methodId methodParams]]",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "from",
Usage: "account to send the cancel message from",
},
},
Action: func(cctx *cli.Context) error {
2022-09-14 18:33:29 +00:00
if cctx.NArg() < 2 {
return ShowHelp(cctx, fmt.Errorf("must pass at least multisig address and message ID"))
}
2022-09-14 18:33:29 +00:00
if cctx.NArg() > 2 && cctx.NArg() < 4 {
return ShowHelp(cctx, fmt.Errorf("usage: msig cancel <msig addr> <message ID> <desination> <value>"))
}
2022-09-14 18:33:29 +00:00
if cctx.NArg() > 4 && cctx.NArg() != 6 {
return ShowHelp(cctx, fmt.Errorf("usage: msig cancel <msig addr> <message ID> <desination> <value> [ <method> <params> ]"))
}
srv, err := GetFullNodeServices(cctx)
if err != nil {
return err
}
defer srv.Close() //nolint:errcheck
api := srv.FullNodeAPI()
ctx := ReqContext(cctx)
msig, err := address.NewFromString(cctx.Args().Get(0))
if err != nil {
return err
}
txid, err := strconv.ParseUint(cctx.Args().Get(1), 10, 64)
if err != nil {
return err
}
var from address.Address
if cctx.IsSet("from") {
f, err := address.NewFromString(cctx.String("from"))
if err != nil {
return err
}
from = f
} else {
defaddr, err := api.WalletDefaultAddress(ctx)
if err != nil {
return err
}
from = defaddr
}
var msgCid cid.Cid
2022-09-14 18:33:29 +00:00
if cctx.NArg() == 2 {
proto, err := api.MsigCancel(ctx, msig, txid, from)
if err != nil {
return err
}
sm, err := InteractiveSend(ctx, cctx, srv, proto)
if err != nil {
return err
}
msgCid = sm.Cid()
} else {
dest, err := address.NewFromString(cctx.Args().Get(2))
if err != nil {
return err
}
value, err := types.ParseFIL(cctx.Args().Get(3))
if err != nil {
return err
}
var method uint64
var params []byte
2022-09-14 18:33:29 +00:00
if cctx.NArg() == 6 {
m, err := strconv.ParseUint(cctx.Args().Get(4), 10, 64)
if err != nil {
return err
}
method = m
p, err := hex.DecodeString(cctx.Args().Get(5))
if err != nil {
return err
}
params = p
}
proto, err := api.MsigCancelTxnHash(ctx, msig, txid, dest, types.BigInt(value), from, method, params)
if err != nil {
return err
}
sm, err := InteractiveSend(ctx, cctx, srv, proto)
if err != nil {
return err
}
msgCid = sm.Cid()
}
fmt.Println("sent cancel in message: ", msgCid)
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
if err != nil {
return err
}
if wait.Receipt.ExitCode.IsError() {
return fmt.Errorf("cancel returned exit %d", wait.Receipt.ExitCode)
}
return nil
},
}
var msigRemoveProposeCmd = &cli.Command{
Name: "propose-remove",
Usage: "Propose to remove a signer",
ArgsUsage: "[multisigAddress signer]",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "decrease-threshold",
Usage: "whether the number of required signers should be decreased",
},
&cli.StringFlag{
Name: "from",
Usage: "account to send the propose message from",
},
},
Action: func(cctx *cli.Context) error {
2022-09-14 18:33:29 +00:00
if cctx.NArg() != 2 {
return IncorrectNumArgs(cctx)
}
srv, err := GetFullNodeServices(cctx)
if err != nil {
return err
}
defer srv.Close() //nolint:errcheck
api := srv.FullNodeAPI()
ctx := ReqContext(cctx)
msig, err := address.NewFromString(cctx.Args().Get(0))
if err != nil {
return err
}
addr, err := address.NewFromString(cctx.Args().Get(1))
if err != nil {
return err
}
var from address.Address
if cctx.IsSet("from") {
f, err := address.NewFromString(cctx.String("from"))
if err != nil {
return err
}
from = f
} else {
defaddr, err := api.WalletDefaultAddress(ctx)
if err != nil {
return err
}
from = defaddr
}
proto, err := api.MsigRemoveSigner(ctx, msig, from, addr, cctx.Bool("decrease-threshold"))
if err != nil {
return err
}
sm, err := InteractiveSend(ctx, cctx, srv, proto)
if err != nil {
return err
}
msgCid := sm.Cid()
fmt.Println("sent remove proposal in message: ", msgCid)
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
if err != nil {
return err
}
if wait.Receipt.ExitCode.IsError() {
return fmt.Errorf("add proposal returned exit %d", wait.Receipt.ExitCode)
}
var ret multisig.ProposeReturn
err = ret.UnmarshalCBOR(bytes.NewReader(wait.Receipt.Return))
if err != nil {
return xerrors.Errorf("decoding proposal return: %w", err)
}
fmt.Printf("TxnID: %d", ret.TxnID)
return nil
},
}
var msigAddProposeCmd = &cli.Command{
Name: "add-propose",
Usage: "Propose to add a signer",
ArgsUsage: "[multisigAddress signer]",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "increase-threshold",
Usage: "whether the number of required signers should be increased",
},
&cli.StringFlag{
Name: "from",
Usage: "account to send the propose message from",
},
},
Action: func(cctx *cli.Context) error {
2022-09-14 18:33:29 +00:00
if cctx.NArg() != 2 {
return IncorrectNumArgs(cctx)
}
srv, err := GetFullNodeServices(cctx)
if err != nil {
return err
}
defer srv.Close() //nolint:errcheck
api := srv.FullNodeAPI()
ctx := ReqContext(cctx)
msig, err := address.NewFromString(cctx.Args().Get(0))
if err != nil {
return err
}
addr, err := address.NewFromString(cctx.Args().Get(1))
if err != nil {
return err
}
var from address.Address
if cctx.IsSet("from") {
f, err := address.NewFromString(cctx.String("from"))
if err != nil {
return err
}
from = f
} else {
defaddr, err := api.WalletDefaultAddress(ctx)
if err != nil {
return err
}
from = defaddr
}
2022-06-09 16:36:46 +00:00
store := adt.WrapStore(ctx, cbor.NewCborStore(blockstore.NewAPIBlockstore(api)))
head, err := api.ChainHead(ctx)
if err != nil {
return err
}
act, err := api.StateGetActor(ctx, msig, head.Key())
if err != nil {
return err
}
mstate, err := multisig.Load(store, act)
if err != nil {
return err
}
signers, err := mstate.Signers()
if err != nil {
return err
}
2022-06-21 09:49:10 +00:00
2022-06-14 22:59:17 +00:00
addrId, err := api.StateLookupID(ctx, addr, types.EmptyTSK)
if err != nil {
return err
}
2022-06-21 09:49:10 +00:00
2022-06-09 16:36:46 +00:00
for _, s := range signers {
2022-06-14 03:17:04 +00:00
if s == addrId {
return fmt.Errorf("%s is already a signer", addr.String())
2022-06-09 16:36:46 +00:00
}
}
proto, err := api.MsigAddPropose(ctx, msig, from, addr, cctx.Bool("increase-threshold"))
if err != nil {
return err
}
sm, err := InteractiveSend(ctx, cctx, srv, proto)
if err != nil {
return err
}
msgCid := sm.Cid()
2020-10-15 10:45:02 +00:00
fmt.Fprintln(cctx.App.Writer, "sent add proposal in message: ", msgCid)
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
if err != nil {
return err
}
if wait.Receipt.ExitCode.IsError() {
return fmt.Errorf("add proposal returned exit %d", wait.Receipt.ExitCode)
}
return nil
},
}
var msigAddApproveCmd = &cli.Command{
Name: "add-approve",
Usage: "Approve a message to add a signer",
ArgsUsage: "[multisigAddress proposerAddress txId newAddress increaseThreshold]",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "from",
Usage: "account to send the approve message from",
},
},
Action: func(cctx *cli.Context) error {
2022-09-14 18:33:29 +00:00
if cctx.NArg() != 5 {
return IncorrectNumArgs(cctx)
}
srv, err := GetFullNodeServices(cctx)
if err != nil {
return err
}
defer srv.Close() //nolint:errcheck
api := srv.FullNodeAPI()
ctx := ReqContext(cctx)
msig, err := address.NewFromString(cctx.Args().Get(0))
if err != nil {
return err
}
prop, err := address.NewFromString(cctx.Args().Get(1))
if err != nil {
return err
}
txid, err := strconv.ParseUint(cctx.Args().Get(2), 10, 64)
if err != nil {
return err
}
newAdd, err := address.NewFromString(cctx.Args().Get(3))
if err != nil {
return err
}
inc, err := strconv.ParseBool(cctx.Args().Get(4))
if err != nil {
return err
}
var from address.Address
if cctx.IsSet("from") {
f, err := address.NewFromString(cctx.String("from"))
if err != nil {
return err
}
from = f
} else {
defaddr, err := api.WalletDefaultAddress(ctx)
if err != nil {
return err
}
from = defaddr
}
proto, err := api.MsigAddApprove(ctx, msig, from, txid, prop, newAdd, inc)
if err != nil {
return err
}
sm, err := InteractiveSend(ctx, cctx, srv, proto)
if err != nil {
return err
}
msgCid := sm.Cid()
fmt.Println("sent add approval in message: ", msgCid)
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
if err != nil {
return err
}
if wait.Receipt.ExitCode.IsError() {
return fmt.Errorf("add approval returned exit %d", wait.Receipt.ExitCode)
}
return nil
},
}
var msigAddCancelCmd = &cli.Command{
Name: "add-cancel",
Usage: "Cancel a message to add a signer",
ArgsUsage: "[multisigAddress txId newAddress increaseThreshold]",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "from",
Usage: "account to send the approve message from",
},
},
Action: func(cctx *cli.Context) error {
2022-09-14 18:33:29 +00:00
if cctx.NArg() != 4 {
return IncorrectNumArgs(cctx)
}
srv, err := GetFullNodeServices(cctx)
if err != nil {
return err
}
defer srv.Close() //nolint:errcheck
api := srv.FullNodeAPI()
ctx := ReqContext(cctx)
msig, err := address.NewFromString(cctx.Args().Get(0))
if err != nil {
return err
}
txid, err := strconv.ParseUint(cctx.Args().Get(1), 10, 64)
if err != nil {
return err
}
newAdd, err := address.NewFromString(cctx.Args().Get(2))
if err != nil {
return err
}
inc, err := strconv.ParseBool(cctx.Args().Get(3))
if err != nil {
return err
}
var from address.Address
if cctx.IsSet("from") {
f, err := address.NewFromString(cctx.String("from"))
if err != nil {
return err
}
from = f
} else {
defaddr, err := api.WalletDefaultAddress(ctx)
if err != nil {
return err
}
from = defaddr
}
proto, err := api.MsigAddCancel(ctx, msig, from, txid, newAdd, inc)
if err != nil {
return err
}
sm, err := InteractiveSend(ctx, cctx, srv, proto)
if err != nil {
return err
}
msgCid := sm.Cid()
fmt.Println("sent add cancellation in message: ", msgCid)
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
if err != nil {
return err
}
if wait.Receipt.ExitCode.IsError() {
return fmt.Errorf("add cancellation returned exit %d", wait.Receipt.ExitCode)
}
return nil
},
}
2020-07-16 00:55:27 +00:00
var msigSwapProposeCmd = &cli.Command{
Name: "swap-propose",
Usage: "Propose to swap signers",
ArgsUsage: "[multisigAddress oldAddress newAddress]",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "from",
Usage: "account to send the approve message from",
},
},
Action: func(cctx *cli.Context) error {
2022-09-14 18:33:29 +00:00
if cctx.NArg() != 3 {
return IncorrectNumArgs(cctx)
}
srv, err := GetFullNodeServices(cctx)
2020-07-16 00:55:27 +00:00
if err != nil {
return err
}
defer srv.Close() //nolint:errcheck
api := srv.FullNodeAPI()
2020-07-16 00:55:27 +00:00
ctx := ReqContext(cctx)
msig, err := address.NewFromString(cctx.Args().Get(0))
if err != nil {
return err
}
oldAdd, err := address.NewFromString(cctx.Args().Get(1))
if err != nil {
return err
}
newAdd, err := address.NewFromString(cctx.Args().Get(2))
if err != nil {
return err
}
var from address.Address
if cctx.IsSet("from") {
f, err := address.NewFromString(cctx.String("from"))
if err != nil {
return err
}
from = f
} else {
defaddr, err := api.WalletDefaultAddress(ctx)
if err != nil {
return err
}
from = defaddr
}
proto, err := api.MsigSwapPropose(ctx, msig, from, oldAdd, newAdd)
2020-07-16 00:55:27 +00:00
if err != nil {
return err
}
sm, err := InteractiveSend(ctx, cctx, srv, proto)
if err != nil {
return err
}
msgCid := sm.Cid()
2020-07-16 00:55:27 +00:00
fmt.Println("sent swap proposal in message: ", msgCid)
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
2020-07-16 00:55:27 +00:00
if err != nil {
return err
}
if wait.Receipt.ExitCode.IsError() {
2020-07-16 00:55:27 +00:00
return fmt.Errorf("swap proposal returned exit %d", wait.Receipt.ExitCode)
}
return nil
},
}
var msigSwapApproveCmd = &cli.Command{
Name: "swap-approve",
Usage: "Approve a message to swap signers",
ArgsUsage: "[multisigAddress proposerAddress txId oldAddress newAddress]",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "from",
Usage: "account to send the approve message from",
},
},
Action: func(cctx *cli.Context) error {
2022-09-14 18:33:29 +00:00
if cctx.NArg() != 5 {
return IncorrectNumArgs(cctx)
}
srv, err := GetFullNodeServices(cctx)
2020-07-16 00:55:27 +00:00
if err != nil {
return err
}
defer srv.Close() //nolint:errcheck
api := srv.FullNodeAPI()
2020-07-16 00:55:27 +00:00
ctx := ReqContext(cctx)
msig, err := address.NewFromString(cctx.Args().Get(0))
if err != nil {
return err
}
prop, err := address.NewFromString(cctx.Args().Get(1))
if err != nil {
return err
}
txid, err := strconv.ParseUint(cctx.Args().Get(2), 10, 64)
if err != nil {
return err
}
oldAdd, err := address.NewFromString(cctx.Args().Get(3))
if err != nil {
return err
}
newAdd, err := address.NewFromString(cctx.Args().Get(4))
if err != nil {
return err
}
var from address.Address
if cctx.IsSet("from") {
f, err := address.NewFromString(cctx.String("from"))
if err != nil {
return err
}
from = f
} else {
defaddr, err := api.WalletDefaultAddress(ctx)
if err != nil {
return err
}
from = defaddr
}
proto, err := api.MsigSwapApprove(ctx, msig, from, txid, prop, oldAdd, newAdd)
2020-07-16 00:55:27 +00:00
if err != nil {
return err
}
sm, err := InteractiveSend(ctx, cctx, srv, proto)
if err != nil {
return err
}
msgCid := sm.Cid()
2020-07-16 00:55:27 +00:00
fmt.Println("sent swap approval in message: ", msgCid)
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
2020-07-16 00:55:27 +00:00
if err != nil {
return err
}
if wait.Receipt.ExitCode.IsError() {
2020-07-16 00:55:27 +00:00
return fmt.Errorf("swap approval returned exit %d", wait.Receipt.ExitCode)
}
return nil
},
}
var msigSwapCancelCmd = &cli.Command{
Name: "swap-cancel",
Usage: "Cancel a message to swap signers",
ArgsUsage: "[multisigAddress txId oldAddress newAddress]",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "from",
Usage: "account to send the approve message from",
},
},
Action: func(cctx *cli.Context) error {
2022-09-14 18:33:29 +00:00
if cctx.NArg() != 4 {
return IncorrectNumArgs(cctx)
}
srv, err := GetFullNodeServices(cctx)
2020-07-16 00:55:27 +00:00
if err != nil {
return err
}
defer srv.Close() //nolint:errcheck
api := srv.FullNodeAPI()
2020-07-16 00:55:27 +00:00
ctx := ReqContext(cctx)
msig, err := address.NewFromString(cctx.Args().Get(0))
if err != nil {
return err
}
txid, err := strconv.ParseUint(cctx.Args().Get(1), 10, 64)
if err != nil {
return err
}
oldAdd, err := address.NewFromString(cctx.Args().Get(2))
if err != nil {
return err
}
newAdd, err := address.NewFromString(cctx.Args().Get(3))
if err != nil {
return err
}
var from address.Address
if cctx.IsSet("from") {
f, err := address.NewFromString(cctx.String("from"))
if err != nil {
return err
}
from = f
} else {
defaddr, err := api.WalletDefaultAddress(ctx)
if err != nil {
return err
}
from = defaddr
}
proto, err := api.MsigSwapCancel(ctx, msig, from, txid, oldAdd, newAdd)
if err != nil {
return err
}
sm, err := InteractiveSend(ctx, cctx, srv, proto)
2020-07-16 00:55:27 +00:00
if err != nil {
return err
}
msgCid := sm.Cid()
fmt.Println("sent swap cancellation in message: ", msgCid)
2020-07-16 00:55:27 +00:00
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
2020-07-16 00:55:27 +00:00
if err != nil {
return err
}
if wait.Receipt.ExitCode.IsError() {
return fmt.Errorf("swap cancellation returned exit %d", wait.Receipt.ExitCode)
2020-07-16 00:55:27 +00:00
}
return nil
},
}
var msigLockProposeCmd = &cli.Command{
Name: "lock-propose",
Usage: "Propose to lock up some balance",
ArgsUsage: "[multisigAddress startEpoch unlockDuration amount]",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "from",
2020-09-24 16:46:21 +00:00
Usage: "account to send the propose message from",
},
},
Action: func(cctx *cli.Context) error {
2022-09-14 18:33:29 +00:00
if cctx.NArg() != 4 {
return IncorrectNumArgs(cctx)
}
srv, err := GetFullNodeServices(cctx)
if err != nil {
return err
}
defer srv.Close() //nolint:errcheck
api := srv.FullNodeAPI()
ctx := ReqContext(cctx)
msig, err := address.NewFromString(cctx.Args().Get(0))
if err != nil {
return err
}
start, err := strconv.ParseUint(cctx.Args().Get(1), 10, 64)
if err != nil {
return err
}
duration, err := strconv.ParseUint(cctx.Args().Get(2), 10, 64)
if err != nil {
return err
}
amount, err := types.ParseFIL(cctx.Args().Get(3))
if err != nil {
return err
}
var from address.Address
if cctx.IsSet("from") {
f, err := address.NewFromString(cctx.String("from"))
if err != nil {
return err
}
from = f
} else {
defaddr, err := api.WalletDefaultAddress(ctx)
if err != nil {
return err
}
from = defaddr
}
2020-10-08 01:09:33 +00:00
params, actErr := actors.SerializeParams(&msig2.LockBalanceParams{
StartEpoch: abi.ChainEpoch(start),
UnlockDuration: abi.ChainEpoch(duration),
2020-12-15 21:27:22 +00:00
Amount: big.Int(amount),
})
if actErr != nil {
return actErr
}
proto, err := api.MsigPropose(ctx, msig, msig, big.Zero(), from, uint64(multisig.Methods.LockBalance), params)
if err != nil {
return err
}
sm, err := InteractiveSend(ctx, cctx, srv, proto)
if err != nil {
return err
}
msgCid := sm.Cid()
fmt.Println("sent lock proposal in message: ", msgCid)
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
if err != nil {
return err
}
if wait.Receipt.ExitCode.IsError() {
return fmt.Errorf("lock proposal returned exit %d", wait.Receipt.ExitCode)
}
return nil
},
}
var msigLockApproveCmd = &cli.Command{
Name: "lock-approve",
Usage: "Approve a message to lock up some balance",
ArgsUsage: "[multisigAddress proposerAddress txId startEpoch unlockDuration amount]",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "from",
Usage: "account to send the approve message from",
},
},
Action: func(cctx *cli.Context) error {
2022-09-14 18:33:29 +00:00
if cctx.NArg() != 6 {
return IncorrectNumArgs(cctx)
}
srv, err := GetFullNodeServices(cctx)
if err != nil {
return err
}
defer srv.Close() //nolint:errcheck
api := srv.FullNodeAPI()
ctx := ReqContext(cctx)
msig, err := address.NewFromString(cctx.Args().Get(0))
if err != nil {
return err
}
prop, err := address.NewFromString(cctx.Args().Get(1))
if err != nil {
return err
}
txid, err := strconv.ParseUint(cctx.Args().Get(2), 10, 64)
if err != nil {
return err
}
start, err := strconv.ParseUint(cctx.Args().Get(3), 10, 64)
if err != nil {
return err
}
duration, err := strconv.ParseUint(cctx.Args().Get(4), 10, 64)
if err != nil {
return err
}
amount, err := types.ParseFIL(cctx.Args().Get(5))
if err != nil {
return err
}
var from address.Address
if cctx.IsSet("from") {
f, err := address.NewFromString(cctx.String("from"))
if err != nil {
return err
}
from = f
} else {
defaddr, err := api.WalletDefaultAddress(ctx)
if err != nil {
return err
}
from = defaddr
}
2020-10-08 01:09:33 +00:00
params, actErr := actors.SerializeParams(&msig2.LockBalanceParams{
StartEpoch: abi.ChainEpoch(start),
UnlockDuration: abi.ChainEpoch(duration),
Amount: big.Int(amount),
})
if actErr != nil {
return actErr
}
proto, err := api.MsigApproveTxnHash(ctx, msig, txid, prop, msig, big.Zero(), from, uint64(multisig.Methods.LockBalance), params)
if err != nil {
return err
}
sm, err := InteractiveSend(ctx, cctx, srv, proto)
if err != nil {
return err
}
msgCid := sm.Cid()
fmt.Println("sent lock approval in message: ", msgCid)
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
if err != nil {
return err
}
if wait.Receipt.ExitCode.IsError() {
return fmt.Errorf("lock approval returned exit %d", wait.Receipt.ExitCode)
}
return nil
},
}
var msigLockCancelCmd = &cli.Command{
Name: "lock-cancel",
Usage: "Cancel a message to lock up some balance",
ArgsUsage: "[multisigAddress txId startEpoch unlockDuration amount]",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "from",
2020-09-24 16:46:21 +00:00
Usage: "account to send the cancel message from",
},
},
Action: func(cctx *cli.Context) error {
2022-09-14 18:33:29 +00:00
if cctx.NArg() != 5 {
return IncorrectNumArgs(cctx)
}
srv, err := GetFullNodeServices(cctx)
if err != nil {
return err
}
defer srv.Close() //nolint:errcheck
api := srv.FullNodeAPI()
ctx := ReqContext(cctx)
msig, err := address.NewFromString(cctx.Args().Get(0))
if err != nil {
return err
}
txid, err := strconv.ParseUint(cctx.Args().Get(1), 10, 64)
if err != nil {
return err
}
start, err := strconv.ParseUint(cctx.Args().Get(2), 10, 64)
if err != nil {
return err
}
duration, err := strconv.ParseUint(cctx.Args().Get(3), 10, 64)
if err != nil {
return err
}
amount, err := types.ParseFIL(cctx.Args().Get(4))
if err != nil {
return err
}
var from address.Address
if cctx.IsSet("from") {
f, err := address.NewFromString(cctx.String("from"))
if err != nil {
return err
}
from = f
} else {
defaddr, err := api.WalletDefaultAddress(ctx)
if err != nil {
return err
}
from = defaddr
}
2020-10-08 01:09:33 +00:00
params, actErr := actors.SerializeParams(&msig2.LockBalanceParams{
StartEpoch: abi.ChainEpoch(start),
UnlockDuration: abi.ChainEpoch(duration),
Amount: big.Int(amount),
})
if actErr != nil {
return actErr
}
proto, err := api.MsigCancelTxnHash(ctx, msig, txid, msig, big.Zero(), from, uint64(multisig.Methods.LockBalance), params)
if err != nil {
return err
}
sm, err := InteractiveSend(ctx, cctx, srv, proto)
if err != nil {
return err
}
msgCid := sm.Cid()
fmt.Println("sent lock cancellation in message: ", msgCid)
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
if err != nil {
return err
}
if wait.Receipt.ExitCode.IsError() {
return fmt.Errorf("lock cancellation returned exit %d", wait.Receipt.ExitCode)
}
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 {
2022-09-14 18:33:29 +00:00
if cctx.NArg() != 1 {
return IncorrectNumArgs(cctx)
2020-07-16 00:55:27 +00:00
}
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())
2020-07-16 00:55:27 +00:00
return nil
},
}
var msigProposeThresholdCmd = &cli.Command{
Name: "propose-threshold",
Usage: "Propose setting a different signing threshold on the account",
ArgsUsage: "<multisigAddress newM>",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "from",
Usage: "account to send the proposal from",
},
},
Action: func(cctx *cli.Context) error {
2022-09-14 18:33:29 +00:00
if cctx.NArg() != 2 {
return IncorrectNumArgs(cctx)
}
srv, err := GetFullNodeServices(cctx)
if err != nil {
return err
}
defer srv.Close() //nolint:errcheck
api := srv.FullNodeAPI()
ctx := ReqContext(cctx)
msig, err := address.NewFromString(cctx.Args().Get(0))
if err != nil {
return err
}
2020-10-15 06:16:31 +00:00
newM, err := strconv.ParseUint(cctx.Args().Get(1), 10, 64)
if err != nil {
return err
}
var from address.Address
if cctx.IsSet("from") {
f, err := address.NewFromString(cctx.String("from"))
if err != nil {
return err
}
from = f
} else {
defaddr, err := api.WalletDefaultAddress(ctx)
if err != nil {
return err
}
from = defaddr
}
2020-10-08 01:09:33 +00:00
params, actErr := actors.SerializeParams(&msig2.ChangeNumApprovalsThresholdParams{
NewThreshold: newM,
})
if actErr != nil {
return actErr
}
proto, err := api.MsigPropose(ctx, msig, msig, types.NewInt(0), from, uint64(multisig.Methods.ChangeNumApprovalsThreshold), params)
if err != nil {
return fmt.Errorf("failed to propose change of threshold: %w", err)
}
sm, err := InteractiveSend(ctx, cctx, srv, proto)
if err != nil {
return err
}
msgCid := sm.Cid()
fmt.Println("sent change threshold proposal in message: ", msgCid)
wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true)
if err != nil {
return err
}
if wait.Receipt.ExitCode.IsError() {
return fmt.Errorf("change threshold proposal returned exit %d", wait.Receipt.ExitCode)
}
return nil
},
}