From 2a2b63086a644cd9e02b65f43a918b5754a689f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 4 May 2020 18:03:54 +0200 Subject: [PATCH 1/6] compute-state: Add html output option --- cli/state.go | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/cli/state.go b/cli/state.go index 2ac2cd37c..e4d28d1af 100644 --- a/cli/state.go +++ b/cli/state.go @@ -769,6 +769,10 @@ var stateComputeStateCmd = &cli.Command{ Name: "show-trace", Usage: "print out full execution trace for given tipset", }, + &cli.BoolFlag{ + Name: "html", + Usage: "generate html report", + }, }, Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) @@ -818,6 +822,10 @@ var stateComputeStateCmd = &cli.Command{ return err } + if cctx.Bool("html") { + return computeStateHtml(stout) + } + fmt.Println("computed state cid: ", stout.Root) if cctx.Bool("show-trace") { for _, ir := range stout.Trace { @@ -836,6 +844,65 @@ func printInternalExecutions(prefix string, trace []*types.ExecutionResult) { } } +func computeStateHtml(o *api.ComputeStateOutput) error { + fmt.Printf(` + + + + +
State CID: %s
+
Calls
`, o.Root) + + for _, ir := range o.Trace { + fmt.Printf(`
+
%s -> %s (%s FIL), Method %d
+
Params: %x
+
Exit: %d, Return: %x
+`, ir.Msg.From, ir.Msg.To, types.FIL(ir.Msg.Value), ir.Msg.Method, ir.Msg.Params, ir.MsgRct.ExitCode, ir.MsgRct.ExitCode, ir.MsgRct.Return) + if ir.MsgRct.ExitCode != 0 { + fmt.Printf(`
Error:
%s
`, ir.Error) + } + + if len(ir.InternalExecutions) > 0 { + fmt.Println("
Internal executions:
") + printInternalExecutionsHtml(ir.InternalExecutions) + } + fmt.Println("
") + } + + fmt.Printf(` +`) + return nil +} + +func printInternalExecutionsHtml(trace []*types.ExecutionResult) { + for _, im := range trace { + fmt.Printf(`
+
%s -> %s (%s FIL), Method %d
+
Params: %x
+
Exit: %d, Return: %x
+`, im.Msg.From, im.Msg.To, types.FIL(im.Msg.Value), im.Msg.Method, im.Msg.Params, im.MsgRct.ExitCode, im.MsgRct.ExitCode, im.MsgRct.Return) + if im.MsgRct.ExitCode != 0 { + fmt.Printf(`
Error:
%s
`, im.Error) + } + if len(im.Subcalls) > 0 { + fmt.Println("
Subcalls:
") + printInternalExecutionsHtml(im.Subcalls) + } + fmt.Println("
") + } +} + var stateWaitMsgCmd = &cli.Command{ Name: "wait-msg", Usage: "Wait for a message to appear on chain", From de701a06deb06750e1b65bb71018b343cb6517ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 4 May 2020 18:48:50 +0200 Subject: [PATCH 2/6] compute-state: html: decode code / method names --- cli/state.go | 110 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 91 insertions(+), 19 deletions(-) diff --git a/cli/state.go b/cli/state.go index e4d28d1af..51e1a5655 100644 --- a/cli/state.go +++ b/cli/state.go @@ -9,28 +9,56 @@ import ( "strconv" "strings" - "github.com/filecoin-project/specs-actors/actors/builtin" + "github.com/docker/go-units" + "github.com/ipfs/go-cid" + "github.com/libp2p/go-libp2p-core/peer" + "github.com/multiformats/go-multihash" + cbg "github.com/whyrusleeping/cbor-gen" + "golang.org/x/xerrors" + "gopkg.in/urfave/cli.v2" "github.com/filecoin-project/go-address" "github.com/filecoin-project/specs-actors/actors/abi" + "github.com/filecoin-project/specs-actors/actors/builtin" "github.com/filecoin-project/specs-actors/actors/builtin/market" miner2 "github.com/filecoin-project/specs-actors/actors/builtin/miner" samsig "github.com/filecoin-project/specs-actors/actors/builtin/multisig" "github.com/filecoin-project/specs-actors/actors/builtin/paych" "github.com/filecoin-project/specs-actors/actors/builtin/power" - "github.com/libp2p/go-libp2p-core/peer" - "golang.org/x/xerrors" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/miner" - - "github.com/docker/go-units" - "github.com/ipfs/go-cid" - cbg "github.com/whyrusleeping/cbor-gen" - "gopkg.in/urfave/cli.v2" ) +var methods = map[cid.Cid][]string{} + +func init() { + cidToMethods := map[cid.Cid]interface{}{ + // builtin.SystemActorCodeID: builtin.MethodsSystem - apparently it doesn't have methods + builtin.InitActorCodeID: builtin.MethodsInit, + builtin.CronActorCodeID: builtin.MethodsCron, + builtin.AccountActorCodeID: builtin.MethodsAccount, + builtin.StoragePowerActorCodeID: builtin.MethodsPower, + builtin.StorageMinerActorCodeID: builtin.MethodsMiner, + builtin.StorageMarketActorCodeID: builtin.MethodsMarket, + builtin.PaymentChannelActorCodeID: builtin.MethodsPaych, + builtin.MultisigActorCodeID: builtin.MethodsMultisig, + builtin.RewardActorCodeID: builtin.MethodsReward, + builtin.VerifiedRegistryActorCodeID: builtin.MethodsVerifiedRegistry, + } + + for c, m := range cidToMethods { + rt := reflect.TypeOf(m) + nf := rt.NumField() + + methods[c] = append(methods[c], "Send") + for i := 0; i < nf; i++ { + methods[c] = append(methods[c], rt.Field(i).Name) + } + } +} + var stateCmd = &cli.Command{ Name: "state", Usage: "Interact with and query filecoin chain state", @@ -823,7 +851,22 @@ var stateComputeStateCmd = &cli.Command{ } if cctx.Bool("html") { - return computeStateHtml(stout) + codeCache := map[address.Address]cid.Cid{} + getCode := func(addr address.Address) (cid.Cid, error) { + if c, found := codeCache[addr]; found { + return c, nil + } + + c, err := api.StateGetActor(ctx, addr, ts.Key()) + if err != nil { + return cid.Cid{}, err + } + + codeCache[addr] = c.Code + return c.Code, nil + } + + return computeStateHtml(stout, getCode) } fmt.Println("computed state cid: ", stout.Root) @@ -844,12 +887,22 @@ func printInternalExecutions(prefix string, trace []*types.ExecutionResult) { } } -func computeStateHtml(o *api.ComputeStateOutput) error { +func codeStr(c cid.Cid) string { + cmh, err := multihash.Decode(c.Hash()) + if err != nil { + panic(err) + } + return string(cmh.Digest) +} + +func computeStateHtml(o *api.ComputeStateOutput, getCode func(addr address.Address) (cid.Cid, error)) error { fmt.Printf(` @@ -864,18 +920,25 @@ func computeStateHtml(o *api.ComputeStateOutput) error {
Calls
`, o.Root) for _, ir := range o.Trace { + toCode, err := getCode(ir.Msg.To) + if err != nil { + return xerrors.Errorf("getting code for %s: %w", toCode, err) + } + fmt.Printf(`
-
%s -> %s (%s FIL), Method %d
-
Params: %x
+
%s -> %s (%s FIL), M%d
+
%s:%s(%x)
Exit: %d, Return: %x
-`, ir.Msg.From, ir.Msg.To, types.FIL(ir.Msg.Value), ir.Msg.Method, ir.Msg.Params, ir.MsgRct.ExitCode, ir.MsgRct.ExitCode, ir.MsgRct.Return) +`, ir.Msg.From, ir.Msg.To, types.FIL(ir.Msg.Value), ir.Msg.Method, codeStr(toCode), methods[toCode][ir.Msg.Method], ir.Msg.Params, ir.Msg.Params, ir.MsgRct.ExitCode, ir.MsgRct.ExitCode, ir.MsgRct.Return) if ir.MsgRct.ExitCode != 0 { fmt.Printf(`
Error:
%s
`, ir.Error) } if len(ir.InternalExecutions) > 0 { fmt.Println("
Internal executions:
") - printInternalExecutionsHtml(ir.InternalExecutions) + if err := printInternalExecutionsHtml(ir.InternalExecutions, getCode); err != nil { + return err + } } fmt.Println("
") } @@ -885,22 +948,31 @@ func computeStateHtml(o *api.ComputeStateOutput) error { return nil } -func printInternalExecutionsHtml(trace []*types.ExecutionResult) { +func printInternalExecutionsHtml(trace []*types.ExecutionResult, getCode func(addr address.Address) (cid.Cid, error)) error { for _, im := range trace { + toCode, err := getCode(im.Msg.To) + if err != nil { + return xerrors.Errorf("getting code for %s: %w", toCode, err) + } + fmt.Printf(`
-
%s -> %s (%s FIL), Method %d
-
Params: %x
+
%s -> %s (%s FIL), M%d
+
%s:%s(%x)
Exit: %d, Return: %x
-`, im.Msg.From, im.Msg.To, types.FIL(im.Msg.Value), im.Msg.Method, im.Msg.Params, im.MsgRct.ExitCode, im.MsgRct.ExitCode, im.MsgRct.Return) +`, im.Msg.From, im.Msg.To, types.FIL(im.Msg.Value), im.Msg.Method, codeStr(toCode), methods[toCode][im.Msg.Method], im.Msg.Params, im.Msg.Params, im.MsgRct.ExitCode, im.MsgRct.ExitCode, im.MsgRct.Return) if im.MsgRct.ExitCode != 0 { fmt.Printf(`
Error:
%s
`, im.Error) } if len(im.Subcalls) > 0 { fmt.Println("
Subcalls:
") - printInternalExecutionsHtml(im.Subcalls) + if err := printInternalExecutionsHtml(im.Subcalls, getCode); err != nil { + return err + } } fmt.Println("
") } + + return nil } var stateWaitMsgCmd = &cli.Command{ From 4f959adbc8bfd7f69155f5ba160acb116e218c8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 4 May 2020 19:19:23 +0200 Subject: [PATCH 3/6] compute-state: html: Print invocation duration --- cli/state.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/cli/state.go b/cli/state.go index 51e1a5655..af1b10df8 100644 --- a/cli/state.go +++ b/cli/state.go @@ -8,6 +8,7 @@ import ( "reflect" "strconv" "strings" + "time" "github.com/docker/go-units" "github.com/ipfs/go-cid" @@ -913,6 +914,8 @@ func computeStateHtml(o *api.ComputeStateOutput, getCode func(addr address.Addre .exec:hover { background: #fef; } + .slow-true-false { color: #660; } + .slow-true-true { color: #f80; } @@ -925,11 +928,14 @@ func computeStateHtml(o *api.ComputeStateOutput, getCode func(addr address.Addre return xerrors.Errorf("getting code for %s: %w", toCode, err) } + slow := ir.Duration > 10 * time.Millisecond + veryslow := ir.Duration > 50 * time.Millisecond + fmt.Printf(`
%s -> %s (%s FIL), M%d
%s:%s(%x)
-
Exit: %d, Return: %x
-`, ir.Msg.From, ir.Msg.To, types.FIL(ir.Msg.Value), ir.Msg.Method, codeStr(toCode), methods[toCode][ir.Msg.Method], ir.Msg.Params, ir.Msg.Params, ir.MsgRct.ExitCode, ir.MsgRct.ExitCode, ir.MsgRct.Return) +
Took %s, Exit: %d, Return: %x
+`, ir.Msg.From, ir.Msg.To, types.FIL(ir.Msg.Value), ir.Msg.Method, codeStr(toCode), methods[toCode][ir.Msg.Method], ir.Msg.Params, ir.Msg.Params, slow, veryslow, ir.Duration, ir.MsgRct.ExitCode, ir.MsgRct.ExitCode, ir.MsgRct.Return) if ir.MsgRct.ExitCode != 0 { fmt.Printf(`
Error:
%s
`, ir.Error) } From 2e0a3fca1a80830cb729f869f9e92179b4a9e9b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 4 May 2020 20:12:31 +0200 Subject: [PATCH 4/6] compute-state: decode returns/params --- cli/state.go | 157 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 126 insertions(+), 31 deletions(-) diff --git a/cli/state.go b/cli/state.go index af1b10df8..15e9f08f0 100644 --- a/cli/state.go +++ b/cli/state.go @@ -21,41 +21,66 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/specs-actors/actors/abi" "github.com/filecoin-project/specs-actors/actors/builtin" + "github.com/filecoin-project/specs-actors/actors/builtin/account" + "github.com/filecoin-project/specs-actors/actors/builtin/cron" + init_ "github.com/filecoin-project/specs-actors/actors/builtin/init" "github.com/filecoin-project/specs-actors/actors/builtin/market" miner2 "github.com/filecoin-project/specs-actors/actors/builtin/miner" - samsig "github.com/filecoin-project/specs-actors/actors/builtin/multisig" + "github.com/filecoin-project/specs-actors/actors/builtin/multisig" "github.com/filecoin-project/specs-actors/actors/builtin/paych" "github.com/filecoin-project/specs-actors/actors/builtin/power" + "github.com/filecoin-project/specs-actors/actors/builtin/reward" + "github.com/filecoin-project/specs-actors/actors/builtin/verifreg" + "github.com/filecoin-project/specs-actors/actors/util/adt" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/miner" ) -var methods = map[cid.Cid][]string{} +type methodMeta struct { + name string + + params reflect.Type + ret reflect.Type +} + +var methods = map[cid.Cid][]methodMeta{} func init() { - cidToMethods := map[cid.Cid]interface{}{ - // builtin.SystemActorCodeID: builtin.MethodsSystem - apparently it doesn't have methods - builtin.InitActorCodeID: builtin.MethodsInit, - builtin.CronActorCodeID: builtin.MethodsCron, - builtin.AccountActorCodeID: builtin.MethodsAccount, - builtin.StoragePowerActorCodeID: builtin.MethodsPower, - builtin.StorageMinerActorCodeID: builtin.MethodsMiner, - builtin.StorageMarketActorCodeID: builtin.MethodsMarket, - builtin.PaymentChannelActorCodeID: builtin.MethodsPaych, - builtin.MultisigActorCodeID: builtin.MethodsMultisig, - builtin.RewardActorCodeID: builtin.MethodsReward, - builtin.VerifiedRegistryActorCodeID: builtin.MethodsVerifiedRegistry, + cidToMethods := map[cid.Cid][2]interface{}{ + // builtin.SystemActorCodeID: {builtin.MethodsSystem, system.Actor{} }- apparently it doesn't have methods + builtin.InitActorCodeID: {builtin.MethodsInit, init_.Actor{}}, + builtin.CronActorCodeID: {builtin.MethodsCron, cron.Actor{}}, + builtin.AccountActorCodeID: {builtin.MethodsAccount, account.Actor{}}, + builtin.StoragePowerActorCodeID: {builtin.MethodsPower, power.Actor{}}, + builtin.StorageMinerActorCodeID: {builtin.MethodsMiner, miner2.Actor{}}, + builtin.StorageMarketActorCodeID: {builtin.MethodsMarket, market.Actor{}}, + builtin.PaymentChannelActorCodeID: {builtin.MethodsPaych, paych.Actor{}}, + builtin.MultisigActorCodeID: {builtin.MethodsMultisig, multisig.Actor{}}, + builtin.RewardActorCodeID: {builtin.MethodsReward, reward.Actor{}}, + builtin.VerifiedRegistryActorCodeID: {builtin.MethodsVerifiedRegistry, verifreg.Actor{}}, } for c, m := range cidToMethods { - rt := reflect.TypeOf(m) + rt := reflect.TypeOf(m[0]) nf := rt.NumField() - methods[c] = append(methods[c], "Send") + methods[c] = append(methods[c], methodMeta{ + name: "Send", + params: reflect.TypeOf(new(adt.EmptyValue)), + ret: reflect.TypeOf(new(adt.EmptyValue)), + }) + + exports := m[1].(abi.Invokee).Exports() for i := 0; i < nf; i++ { - methods[c] = append(methods[c], rt.Field(i).Name) + export := reflect.TypeOf(exports[i+1]) + + methods[c] = append(methods[c], methodMeta{ + name: rt.Field(i).Name, + params: export.In(1), + ret: export.Out(0), + }) } } } @@ -903,16 +928,18 @@ func computeStateHtml(o *api.ComputeStateOutput, getCode func(addr address.Addre html, body { font-family: monospace; } a:link, a:visited { color: #004; } pre { background: #ccc; } - code { background: #eee; } + .call { color: #00a; } + .params { background: #dfd; } + .ret { background: #ddf; } .error { color: red; } .exit0 { color: green; } .exec { - padding-left: 10px; - border-left: 1px solid; - margin-bottom: 15px; + padding-left: 15px; + border-left: 2.5px solid; + margin-bottom: 45px; } .exec:hover { - background: #fef; + background: #eee; } .slow-true-false { color: #660; } .slow-true-true { color: #f80; } @@ -928,14 +955,37 @@ func computeStateHtml(o *api.ComputeStateOutput, getCode func(addr address.Addre return xerrors.Errorf("getting code for %s: %w", toCode, err) } - slow := ir.Duration > 10 * time.Millisecond - veryslow := ir.Duration > 50 * time.Millisecond + params, err := jsonParams(toCode, ir.Msg.Method, ir.Msg.Params) + if err != nil { + return xerrors.Errorf("decoding params: %w", err) + } + + if len(ir.Msg.Params) != 0 { + params = `
` + params + `
` + } else { + params = "" + } + + ret, err := jsonReturn(toCode, ir.Msg.Method, ir.MsgRct.Return) + if err != nil { + return xerrors.Errorf("decoding return value: %w", err) + } + + if len(ir.MsgRct.Return) == 0 { + ret = "
" + } else { + ret = `, Return
` + ret + `
` + } + + slow := ir.Duration > 10*time.Millisecond + veryslow := ir.Duration > 50*time.Millisecond fmt.Printf(`
+

%s:%s

%s -> %s (%s FIL), M%d
-
%s:%s(%x)
-
Took %s, Exit: %d, Return: %x
-`, ir.Msg.From, ir.Msg.To, types.FIL(ir.Msg.Value), ir.Msg.Method, codeStr(toCode), methods[toCode][ir.Msg.Method], ir.Msg.Params, ir.Msg.Params, slow, veryslow, ir.Duration, ir.MsgRct.ExitCode, ir.MsgRct.ExitCode, ir.MsgRct.Return) +%s +
Took %s, Exit: %d%s +`, codeStr(toCode), methods[toCode][ir.Msg.Method].name, ir.Msg.From, ir.Msg.To, types.FIL(ir.Msg.Value), ir.Msg.Method, params, slow, veryslow, ir.Duration, ir.MsgRct.ExitCode, ir.MsgRct.ExitCode, ret) if ir.MsgRct.ExitCode != 0 { fmt.Printf(`
Error:
%s
`, ir.Error) } @@ -961,11 +1011,34 @@ func printInternalExecutionsHtml(trace []*types.ExecutionResult, getCode func(ad return xerrors.Errorf("getting code for %s: %w", toCode, err) } + params, err := jsonParams(toCode, im.Msg.Method, im.Msg.Params) + if err != nil { + return xerrors.Errorf("decoding params: %w", err) + } + + if len(im.Msg.Params) != 0 { + params = `
` + params + `
` + } else { + params = "" + } + + ret, err := jsonReturn(toCode, im.Msg.Method, im.MsgRct.Return) + if err != nil { + return xerrors.Errorf("decoding return value: %w", err) + } + + if len(im.MsgRct.Return) == 0 { + ret = "
" + } else { + ret = `, Return
` + ret + `
` + } + fmt.Printf(`
+

%s:%s

%s -> %s (%s FIL), M%d
-
%s:%s(%x)
-
Exit: %d, Return: %x
-`, im.Msg.From, im.Msg.To, types.FIL(im.Msg.Value), im.Msg.Method, codeStr(toCode), methods[toCode][im.Msg.Method], im.Msg.Params, im.Msg.Params, im.MsgRct.ExitCode, im.MsgRct.ExitCode, im.MsgRct.Return) +%s +
Exit: %d%s +`, codeStr(toCode), methods[toCode][im.Msg.Method].name, im.Msg.From, im.Msg.To, types.FIL(im.Msg.Value), im.Msg.Method, params, im.MsgRct.ExitCode, im.MsgRct.ExitCode, ret) if im.MsgRct.ExitCode != 0 { fmt.Printf(`
Error:
%s
`, im.Error) } @@ -981,6 +1054,28 @@ func printInternalExecutionsHtml(trace []*types.ExecutionResult, getCode func(ad return nil } +func jsonParams(code cid.Cid, method abi.MethodNum, params []byte) (string, error) { + re := reflect.New(methods[code][method].params.Elem()) + p := re.Interface().(cbg.CBORUnmarshaler) + if err := p.UnmarshalCBOR(bytes.NewReader(params)); err != nil { + return "", err + } + + b, err := json.MarshalIndent(p, "", " ") + return string(b), err +} + +func jsonReturn(code cid.Cid, method abi.MethodNum, ret []byte) (string, error) { + re := reflect.New(methods[code][method].ret.Elem()) + p := re.Interface().(cbg.CBORUnmarshaler) + if err := p.UnmarshalCBOR(bytes.NewReader(ret)); err != nil { + return "", err + } + + b, err := json.MarshalIndent(p, "", " ") + return string(b), err +} + var stateWaitMsgCmd = &cli.Command{ Name: "wait-msg", Usage: "Wait for a message to appear on chain", @@ -1217,7 +1312,7 @@ func parseParamsForMethod(act cid.Cid, method uint64, args []string) ([]byte, er case builtin.StoragePowerActorCodeID: f = power.Actor{}.Exports()[method] case builtin.MultisigActorCodeID: - f = samsig.Actor{}.Exports()[method] + f = multisig.Actor{}.Exports()[method] case builtin.PaymentChannelActorCodeID: f = paych.Actor{}.Exports()[method] default: From 51f92b6ed02ebc7dad13770b2549de12268bdb6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 4 May 2020 20:13:43 +0200 Subject: [PATCH 5/6] mod tidy --- go.mod | 1 - 1 file changed, 1 deletion(-) diff --git a/go.mod b/go.mod index e3933e034..ec68e13b3 100644 --- a/go.mod +++ b/go.mod @@ -89,7 +89,6 @@ require ( github.com/libp2p/go-libp2p-yamux v0.2.7 github.com/libp2p/go-maddr-filter v0.0.5 github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 - github.com/minio/sha256-simd v0.1.1 github.com/mitchellh/go-homedir v1.1.0 github.com/multiformats/go-base32 v0.0.3 github.com/multiformats/go-multiaddr v0.2.1 From 8770cf2f4b4c827de72e5ff5f92dee8a0d7de7af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 4 May 2020 20:19:01 +0200 Subject: [PATCH 6/6] compute-state: html: Show top-level message CID --- cli/state.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cli/state.go b/cli/state.go index 15e9f08f0..2141c6209 100644 --- a/cli/state.go +++ b/cli/state.go @@ -928,6 +928,7 @@ func computeStateHtml(o *api.ComputeStateOutput, getCode func(addr address.Addre html, body { font-family: monospace; } a:link, a:visited { color: #004; } pre { background: #ccc; } + small { color: #444; } .call { color: #00a; } .params { background: #dfd; } .ret { background: #ddf; } @@ -983,9 +984,10 @@ func computeStateHtml(o *api.ComputeStateOutput, getCode func(addr address.Addre fmt.Printf(`

%s:%s

%s -> %s (%s FIL), M%d
+
Msg CID: %s
%s
Took %s, Exit: %d%s -`, codeStr(toCode), methods[toCode][ir.Msg.Method].name, ir.Msg.From, ir.Msg.To, types.FIL(ir.Msg.Value), ir.Msg.Method, params, slow, veryslow, ir.Duration, ir.MsgRct.ExitCode, ir.MsgRct.ExitCode, ret) +`, codeStr(toCode), methods[toCode][ir.Msg.Method].name, ir.Msg.From, ir.Msg.To, types.FIL(ir.Msg.Value), ir.Msg.Method, ir.Msg.Cid(), params, slow, veryslow, ir.Duration, ir.MsgRct.ExitCode, ir.MsgRct.ExitCode, ret) if ir.MsgRct.ExitCode != 0 { fmt.Printf(`
Error:
%s
`, ir.Error) }