-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5219 from filecoin-project/feat/interactive-wallet
lotus-wallet: Add interactive mode
- Loading branch information
Showing
3 changed files
with
277 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,244 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"crypto/rand" | ||
"encoding/hex" | ||
"encoding/json" | ||
"fmt" | ||
gobig "math/big" | ||
"strings" | ||
"sync" | ||
|
||
"github.com/ipfs/go-cid" | ||
"golang.org/x/xerrors" | ||
|
||
"github.com/filecoin-project/go-address" | ||
"github.com/filecoin-project/go-jsonrpc" | ||
"github.com/filecoin-project/go-state-types/big" | ||
"github.com/filecoin-project/go-state-types/crypto" | ||
|
||
"github.com/filecoin-project/lotus/api" | ||
"github.com/filecoin-project/lotus/chain/actors/builtin" | ||
"github.com/filecoin-project/lotus/chain/actors/builtin/multisig" | ||
"github.com/filecoin-project/lotus/chain/stmgr" | ||
"github.com/filecoin-project/lotus/chain/types" | ||
lcli "github.com/filecoin-project/lotus/cli" | ||
) | ||
|
||
type InteractiveWallet struct { | ||
lk sync.Mutex | ||
|
||
apiGetter func() (api.FullNode, jsonrpc.ClientCloser, error) | ||
under api.WalletAPI | ||
} | ||
|
||
func (c *InteractiveWallet) WalletNew(ctx context.Context, typ types.KeyType) (address.Address, error) { | ||
err := c.accept(func() error { | ||
fmt.Println("-----") | ||
fmt.Println("ACTION: WalletNew - Creating new wallet") | ||
fmt.Printf("TYPE: %s\n", typ) | ||
return nil | ||
}) | ||
if err != nil { | ||
return address.Address{}, err | ||
} | ||
|
||
return c.under.WalletNew(ctx, typ) | ||
} | ||
|
||
func (c *InteractiveWallet) WalletHas(ctx context.Context, addr address.Address) (bool, error) { | ||
return c.under.WalletHas(ctx, addr) | ||
} | ||
|
||
func (c *InteractiveWallet) WalletList(ctx context.Context) ([]address.Address, error) { | ||
return c.under.WalletList(ctx) | ||
} | ||
|
||
func (c *InteractiveWallet) WalletSign(ctx context.Context, k address.Address, msg []byte, meta api.MsgMeta) (*crypto.Signature, error) { | ||
err := c.accept(func() error { | ||
fmt.Println("-----") | ||
fmt.Println("ACTION: WalletSign - Sign a message/deal") | ||
fmt.Printf("ADDRESS: %s\n", k) | ||
fmt.Printf("TYPE: %s\n", meta.Type) | ||
|
||
switch meta.Type { | ||
case api.MTChainMsg: | ||
var cmsg types.Message | ||
if err := cmsg.UnmarshalCBOR(bytes.NewReader(meta.Extra)); err != nil { | ||
return xerrors.Errorf("unmarshalling message: %w", err) | ||
} | ||
|
||
_, bc, err := cid.CidFromBytes(msg) | ||
if err != nil { | ||
return xerrors.Errorf("getting cid from signing bytes: %w", err) | ||
} | ||
|
||
if !cmsg.Cid().Equals(bc) { | ||
return xerrors.Errorf("cid(meta.Extra).bytes() != msg") | ||
} | ||
|
||
jb, err := json.MarshalIndent(&cmsg, "", " ") | ||
if err != nil { | ||
return xerrors.Errorf("json-marshaling the message: %w", err) | ||
} | ||
|
||
fmt.Println("Message JSON:", string(jb)) | ||
|
||
fmt.Println("Value:", types.FIL(cmsg.Value)) | ||
fmt.Println("Max Fees:", types.FIL(cmsg.RequiredFunds())) | ||
fmt.Println("Max Total Cost:", types.FIL(big.Add(cmsg.RequiredFunds(), cmsg.Value))) | ||
|
||
if c.apiGetter != nil { | ||
napi, closer, err := c.apiGetter() | ||
if err != nil { | ||
return xerrors.Errorf("getting node api: %w", err) | ||
} | ||
defer closer() | ||
|
||
toact, err := napi.StateGetActor(ctx, cmsg.To, types.EmptyTSK) | ||
if err != nil { | ||
return xerrors.Errorf("looking up dest actor: %w", err) | ||
} | ||
|
||
fmt.Println("Method:", stmgr.MethodsMap[toact.Code][cmsg.Method].Name) | ||
p, err := lcli.JsonParams(toact.Code, cmsg.Method, cmsg.Params) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
fmt.Println("Params:", p) | ||
|
||
if builtin.IsMultisigActor(toact.Code) && cmsg.Method == multisig.Methods.Propose { | ||
var mp multisig.ProposeParams | ||
if err := mp.UnmarshalCBOR(bytes.NewReader(cmsg.Params)); err != nil { | ||
return xerrors.Errorf("unmarshalling multisig propose params: %w", err) | ||
} | ||
|
||
fmt.Println("\tMultiSig Proposal Value:", types.FIL(mp.Value)) | ||
fmt.Println("\tMultiSig Proposal Hex Params:", hex.EncodeToString(mp.Params)) | ||
|
||
toact, err := napi.StateGetActor(ctx, mp.To, types.EmptyTSK) | ||
if err != nil { | ||
return xerrors.Errorf("looking up msig dest actor: %w", err) | ||
} | ||
|
||
fmt.Println("\tMultiSig Proposal Method:", stmgr.MethodsMap[toact.Code][mp.Method].Name) | ||
p, err := lcli.JsonParams(toact.Code, mp.Method, mp.Params) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
fmt.Println("\tMultiSig Proposal Params:", strings.ReplaceAll(p, "\n", "\n\t")) | ||
} | ||
} else { | ||
fmt.Println("Params: No chain node connection, can't decode params") | ||
} | ||
|
||
case api.MTDealProposal: | ||
return xerrors.Errorf("TODO") // TODO | ||
default: | ||
log.Infow("WalletSign", "address", k, "type", meta.Type) | ||
} | ||
|
||
return nil | ||
}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return c.under.WalletSign(ctx, k, msg, meta) | ||
} | ||
|
||
func (c *InteractiveWallet) WalletExport(ctx context.Context, a address.Address) (*types.KeyInfo, error) { | ||
err := c.accept(func() error { | ||
fmt.Println("-----") | ||
fmt.Println("ACTION: WalletExport - Export private key") | ||
fmt.Printf("ADDRESS: %s\n", a) | ||
return nil | ||
}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return c.under.WalletExport(ctx, a) | ||
} | ||
|
||
func (c *InteractiveWallet) WalletImport(ctx context.Context, ki *types.KeyInfo) (address.Address, error) { | ||
err := c.accept(func() error { | ||
fmt.Println("-----") | ||
fmt.Println("ACTION: WalletImport - Import private key") | ||
fmt.Printf("TYPE: %s\n", ki.Type) | ||
return nil | ||
}) | ||
if err != nil { | ||
return address.Undef, err | ||
} | ||
|
||
return c.under.WalletImport(ctx, ki) | ||
} | ||
|
||
func (c *InteractiveWallet) WalletDelete(ctx context.Context, addr address.Address) error { | ||
err := c.accept(func() error { | ||
fmt.Println("-----") | ||
fmt.Println("ACTION: WalletDelete - Delete a private key") | ||
fmt.Printf("ADDRESS: %s\n", addr) | ||
return nil | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return c.under.WalletDelete(ctx, addr) | ||
} | ||
|
||
func (c *InteractiveWallet) accept(prompt func() error) error { | ||
c.lk.Lock() | ||
defer c.lk.Unlock() | ||
|
||
if err := prompt(); err != nil { | ||
return err | ||
} | ||
|
||
yes := randomYes() | ||
for { | ||
fmt.Printf("\nAccept the above? (%s/No): ", yes) | ||
var a string | ||
if _, err := fmt.Scanln(&a); err != nil { | ||
return err | ||
} | ||
switch a { | ||
case yes: | ||
fmt.Println("approved") | ||
return nil | ||
case "No": | ||
return xerrors.Errorf("action rejected") | ||
} | ||
|
||
fmt.Printf("Type EXACTLY '%s' or 'No'\n", yes) | ||
} | ||
} | ||
|
||
var yeses = []string{ | ||
"yes", | ||
"Yes", | ||
"YES", | ||
"approve", | ||
"Approve", | ||
"accept", | ||
"Accept", | ||
"authorize", | ||
"Authorize", | ||
"confirm", | ||
"Confirm", | ||
} | ||
|
||
func randomYes() string { | ||
i, err := rand.Int(rand.Reader, gobig.NewInt(int64(len(yeses)))) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
return yeses[i.Int64()] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters