337 lines
7.6 KiB
Go
337 lines
7.6 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"github.com/fatih/color"
|
|
"github.com/ipfs/go-cid"
|
|
"github.com/urfave/cli/v2"
|
|
"golang.org/x/xerrors"
|
|
|
|
"github.com/filecoin-project/go-address"
|
|
"github.com/filecoin-project/go-state-types/big"
|
|
"github.com/filecoin-project/lotus/chain/consensus"
|
|
"github.com/filecoin-project/lotus/chain/types"
|
|
lcli "github.com/filecoin-project/lotus/cli"
|
|
"github.com/filecoin-project/specs-actors/v2/actors/builtin/multisig"
|
|
)
|
|
|
|
var msgCmd = &cli.Command{
|
|
Name: "message",
|
|
Aliases: []string{"msg"},
|
|
Usage: "Translate message between various formats",
|
|
ArgsUsage: "Message in any form",
|
|
Flags: []cli.Flag{
|
|
&cli.BoolFlag{
|
|
Name: "exec-trace",
|
|
Usage: "Print the execution trace",
|
|
},
|
|
},
|
|
Action: func(cctx *cli.Context) error {
|
|
if cctx.NArg() != 1 {
|
|
return lcli.IncorrectNumArgs(cctx)
|
|
}
|
|
|
|
msg, err := messageFromString(cctx, cctx.Args().First())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
api, closer, err := lcli.GetFullNodeAPI(cctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer closer()
|
|
|
|
ctx := lcli.ReqContext(cctx)
|
|
|
|
// Get the CID of the message
|
|
mcid := msg.Cid()
|
|
|
|
// Search for the message on-chain
|
|
lookup, err := api.StateSearchMsg(ctx, mcid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if lookup == nil {
|
|
fmt.Println("Message not found on-chain. Continuing...")
|
|
} else {
|
|
// Replay the message to get the execution trace
|
|
res, err := api.StateReplay(ctx, types.EmptyTSK, mcid)
|
|
if err != nil {
|
|
return xerrors.Errorf("replay call failed: %w", err)
|
|
}
|
|
|
|
if cctx.Bool("exec-trace") {
|
|
// Print the execution trace
|
|
color.Green("Execution trace:")
|
|
trace, err := json.MarshalIndent(res.ExecutionTrace, "", " ")
|
|
if err != nil {
|
|
return xerrors.Errorf("marshaling execution trace: %w", err)
|
|
}
|
|
fmt.Println(string(trace))
|
|
fmt.Println()
|
|
|
|
color.Green("Receipt:")
|
|
fmt.Printf("Exit code: %d\n", res.MsgRct.ExitCode)
|
|
fmt.Printf("Return: %x\n", res.MsgRct.Return)
|
|
fmt.Printf("Gas Used: %d\n", res.MsgRct.GasUsed)
|
|
}
|
|
}
|
|
|
|
switch msg := msg.(type) {
|
|
case *types.SignedMessage:
|
|
return printSignedMessage(cctx, msg)
|
|
case *types.Message:
|
|
return printMessage(cctx, msg)
|
|
default:
|
|
return xerrors.Errorf("this error message can't be printed")
|
|
}
|
|
},
|
|
}
|
|
|
|
func printSignedMessage(cctx *cli.Context, smsg *types.SignedMessage) error {
|
|
color.Green("Signed:")
|
|
color.Blue("CID: %s\n", smsg.Cid())
|
|
|
|
b, err := smsg.Serialize()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
color.Magenta("HEX: %x\n", b)
|
|
color.Blue("B64: %s\n", base64.StdEncoding.EncodeToString(b))
|
|
jm, err := json.MarshalIndent(smsg, "", " ")
|
|
if err != nil {
|
|
return xerrors.Errorf("marshaling as json: %w", err)
|
|
}
|
|
|
|
color.Magenta("JSON: %s\n", string(jm))
|
|
fmt.Println()
|
|
fmt.Println("---")
|
|
color.Green("Signed Message Details:")
|
|
fmt.Printf("Signature(hex): %x\n", smsg.Signature.Data)
|
|
fmt.Printf("Signature(b64): %s\n", base64.StdEncoding.EncodeToString(smsg.Signature.Data))
|
|
|
|
sigtype, err := smsg.Signature.Type.Name()
|
|
if err != nil {
|
|
sigtype = err.Error()
|
|
}
|
|
fmt.Printf("Signature type: %d (%s)\n", smsg.Signature.Type, sigtype)
|
|
|
|
fmt.Println("-------")
|
|
return printMessage(cctx, &smsg.Message)
|
|
}
|
|
|
|
func printMessage(cctx *cli.Context, msg *types.Message) error {
|
|
if msg.Version != 0x6d736967 {
|
|
color.Green("Unsigned:")
|
|
color.Yellow("CID: %s\n", msg.Cid())
|
|
|
|
b, err := msg.Serialize()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
color.Cyan("HEX: %x\n", b)
|
|
color.Yellow("B64: %s\n", base64.StdEncoding.EncodeToString(b))
|
|
|
|
jm, err := json.MarshalIndent(msg, "", " ")
|
|
if err != nil {
|
|
return xerrors.Errorf("marshaling as json: %w", err)
|
|
}
|
|
|
|
color.Cyan("JSON: %s\n", string(jm))
|
|
fmt.Println()
|
|
} else {
|
|
color.Green("Msig Propose:")
|
|
pp := &multisig.ProposeParams{
|
|
To: msg.To,
|
|
Value: msg.Value,
|
|
Method: msg.Method,
|
|
Params: msg.Params,
|
|
}
|
|
var b bytes.Buffer
|
|
if err := pp.MarshalCBOR(&b); err != nil {
|
|
return err
|
|
}
|
|
|
|
color.Cyan("HEX: %x\n", b.Bytes())
|
|
color.Yellow("B64: %s\n", base64.StdEncoding.EncodeToString(b.Bytes()))
|
|
jm, err := json.MarshalIndent(pp, "", " ")
|
|
if err != nil {
|
|
return xerrors.Errorf("marshaling as json: %w", err)
|
|
}
|
|
|
|
color.Cyan("JSON: %s\n", string(jm))
|
|
fmt.Println()
|
|
}
|
|
|
|
fmt.Println("---")
|
|
color.Green("Message Details:")
|
|
fmt.Println("Value:", types.FIL(msg.Value))
|
|
fmt.Println("Max Fees:", types.FIL(msg.RequiredFunds()))
|
|
fmt.Println("Max Total Cost:", types.FIL(big.Add(msg.RequiredFunds(), msg.Value)))
|
|
|
|
api, closer, err := lcli.GetFullNodeAPI(cctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer closer()
|
|
ctx := lcli.ReqContext(cctx)
|
|
|
|
toact, err := api.StateGetActor(ctx, msg.To, types.EmptyTSK)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
fmt.Println("Method:", consensus.NewActorRegistry().Methods[toact.Code][msg.Method].Name) // todo use remote
|
|
p, err := lcli.JsonParams(toact.Code, msg.Method, msg.Params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Println("Params:", p)
|
|
|
|
if msg, err := messageFromBytes(cctx, msg.Params); err == nil {
|
|
fmt.Println("---")
|
|
color.Red("Params message:")
|
|
|
|
if err := printMessage(cctx, msg.VMMessage()); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func messageFromString(cctx *cli.Context, smsg string) (types.ChainMsg, error) {
|
|
// a CID is least likely to just decode
|
|
if c, err := cid.Parse(smsg); err == nil {
|
|
return messageFromCID(cctx, c)
|
|
}
|
|
|
|
// try baseX serializations next
|
|
{
|
|
// hex first, some hay strings may be decodable as b64
|
|
if b, err := hex.DecodeString(smsg); err == nil {
|
|
return messageFromBytes(cctx, b)
|
|
}
|
|
|
|
// b64 next
|
|
if b, err := base64.StdEncoding.DecodeString(smsg); err == nil {
|
|
return messageFromBytes(cctx, b)
|
|
}
|
|
|
|
// b64u??
|
|
if b, err := base64.URLEncoding.DecodeString(smsg); err == nil {
|
|
return messageFromBytes(cctx, b)
|
|
}
|
|
}
|
|
|
|
// maybe it's json?
|
|
if _, err := messageFromJson(cctx, []byte(smsg)); err == nil {
|
|
return nil, err
|
|
}
|
|
|
|
// declare defeat
|
|
return nil, xerrors.Errorf("couldn't decode the message")
|
|
}
|
|
|
|
func messageFromJson(cctx *cli.Context, msgb []byte) (types.ChainMsg, error) {
|
|
// Unsigned
|
|
{
|
|
var msg types.Message
|
|
if err := json.Unmarshal(msgb, &msg); err == nil {
|
|
if msg.To != address.Undef {
|
|
return &msg, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
// Signed
|
|
{
|
|
var msg types.SignedMessage
|
|
if err := json.Unmarshal(msgb, &msg); err == nil {
|
|
if msg.Message.To != address.Undef {
|
|
return &msg, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil, xerrors.New("probably not a json-serialized message")
|
|
}
|
|
|
|
func messageFromBytes(cctx *cli.Context, msgb []byte) (types.ChainMsg, error) {
|
|
// Signed
|
|
{
|
|
var msg types.SignedMessage
|
|
if err := msg.UnmarshalCBOR(bytes.NewReader(msgb)); err == nil {
|
|
return &msg, nil
|
|
}
|
|
}
|
|
|
|
// Unsigned
|
|
{
|
|
var msg types.Message
|
|
if err := msg.UnmarshalCBOR(bytes.NewReader(msgb)); err == nil {
|
|
return &msg, nil
|
|
}
|
|
}
|
|
|
|
// Multisig propose?
|
|
{
|
|
var pp multisig.ProposeParams
|
|
if err := pp.UnmarshalCBOR(bytes.NewReader(msgb)); err == nil {
|
|
i, err := address.NewIDAddress(0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &types.Message{
|
|
// Hack(-ish)
|
|
Version: 0x6d736967,
|
|
From: i,
|
|
|
|
To: pp.To,
|
|
Value: pp.Value,
|
|
|
|
Method: pp.Method,
|
|
Params: pp.Params,
|
|
|
|
GasFeeCap: big.Zero(),
|
|
GasPremium: big.Zero(),
|
|
}, nil
|
|
}
|
|
}
|
|
|
|
// Encoded json???
|
|
{
|
|
if msg, err := messageFromJson(cctx, msgb); err == nil {
|
|
return msg, nil
|
|
}
|
|
}
|
|
|
|
return nil, xerrors.New("probably not a cbor-serialized message")
|
|
}
|
|
|
|
func messageFromCID(cctx *cli.Context, c cid.Cid) (types.ChainMsg, error) {
|
|
api, closer, err := lcli.GetFullNodeAPI(cctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defer closer()
|
|
ctx := lcli.ReqContext(cctx)
|
|
|
|
msgb, err := api.ChainReadObj(ctx, c)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return messageFromBytes(cctx, msgb)
|
|
}
|