Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: cli: gas estimation tooling #9654

Merged
merged 4 commits into from
Nov 22, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions build/drand.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ var DrandConfigs = map[DrandEnum]dtypes.DrandConfig{
ChainInfoJSON: `{"public_key":"8cda589f88914aa728fd183f383980b35789ce81b274e5daee1f338b77d02566ef4d3fb0098af1f844f10f9c803c1827","period":25,"genesis_time":1595348225,"hash":"e73b7dc3c4f6a236378220c0dd6aa110eb16eed26c11259606e07ee122838d4f","groupHash":"567d4785122a5a3e75a9bc9911d7ea807dd85ff76b78dc4ff06b075712898607"}`,
},
DrandIncentinet: {
Servers: []string{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's going on here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Due to some weird initialization stuff I do, the old beacon needs that. No idea why, that was the quickest fix for hacky code.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@geoff-vball Can you check if this is needed as part of this PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is needed in the sense that if I remove it everything breaks. Whether or not its the best way to do things another question

"https://dev1.drand.sh",
"https://dev2.drand.sh",
},
ChainInfoJSON: `{"public_key":"8cad0c72c606ab27d36ee06de1d5b2db1faf92e447025ca37575ab3a8aac2eaae83192f846fc9e158bc738423753d000","period":30,"genesis_time":1595873820,"hash":"80c8b872c714f4c00fdd3daa465d5514049f457f01f85a4caf68cdcd394ba039","groupHash":"d9406aaed487f7af71851b4399448e311f2328923d454e971536c05398ce2d9b"}`,
},
}
2 changes: 1 addition & 1 deletion chain/beacon/drand/drand.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func NewDrandBeacon(genesisTs, interval uint64, ps *pubsub.PubSub, config dtypes

client, err := dclient.Wrap(clients, opts...)
if err != nil {
return nil, xerrors.Errorf("creating drand client")
return nil, xerrors.Errorf("creating drand client: %w", err)
}

lc, err := lru.New(1024)
Expand Down
77 changes: 77 additions & 0 deletions chain/stmgr/call.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/go-state-types/network"

"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/blockstore"
Expand Down Expand Up @@ -153,6 +154,82 @@ func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types.
}, err
}

func (sm *StateManager) CallAtStateAndVersion(ctx context.Context, msg *types.Message, ts *types.TipSet, stateCid cid.Cid, v network.Version) (*api.InvocResult, error) {
geoff-vball marked this conversation as resolved.
Show resolved Hide resolved
r := rand.NewStateRand(sm.cs, ts.Cids(), sm.beacon, sm.GetNetworkVersion)
geoff-vball marked this conversation as resolved.
Show resolved Hide resolved

buffStore := blockstore.NewTieredBstore(sm.cs.StateBlockstore(), blockstore.NewMemorySync())
vmopt := &vm.VMOpts{
StateBase: stateCid,
Epoch: ts.Height() + 1,
Rand: r,
Bstore: buffStore,
Actors: sm.tsExec.NewActorRegistry(),
Syscalls: sm.Syscalls,
CircSupplyCalc: sm.GetVMCirculatingSupply,
NetworkVersion: v,
BaseFee: ts.Blocks()[0].ParentBaseFee,
LookbackState: LookbackStateGetterForTipset(sm, ts),
Tracing: true,
}

vmi, err := sm.newVM(ctx, vmopt)
if err != nil {
return nil, xerrors.Errorf("failed to set up vm: %w", err)
}

stTree, err := state.LoadStateTree(cbor.NewCborStore(buffStore), stateCid)
if err != nil {
return nil, xerrors.Errorf("loading state tree: %w", err)
}

fromActor, err := stTree.GetActor(msg.From)
if err != nil {
return nil, xerrors.Errorf("call raw get actor: %s", err)
}

msg.Nonce = fromActor.Nonce

fromKey, err := sm.ResolveToKeyAddress(ctx, msg.From, ts)
if err != nil {
return nil, xerrors.Errorf("could not resolve key: %w", err)
}

var msgApply types.ChainMsg

switch fromKey.Protocol() {
case address.BLS:
msgApply = msg
case address.SECP256K1:
msgApply = &types.SignedMessage{
Message: *msg,
Signature: crypto.Signature{
Type: crypto.SigTypeSecp256k1,
Data: make([]byte, 65),
},
}
}

ret, err := vmi.ApplyMessage(ctx, msgApply)
if err != nil {
return nil, xerrors.Errorf("gas estimation failed: %w", err)
}

var errs string
if ret.ActorErr != nil {
errs = ret.ActorErr.Error()
}

return &api.InvocResult{
MsgCid: msg.Cid(),
Msg: msg,
MsgRct: &ret.MessageReceipt,
GasCost: MakeMsgGasCost(msg, ret),
ExecutionTrace: ret.ExecutionTrace,
Error: errs,
Duration: ret.Duration,
}, nil
}

func (sm *StateManager) CallWithGas(ctx context.Context, msg *types.Message, priorMsgs []types.ChainMsg, ts *types.TipSet) (*api.InvocResult, error) {
ctx, span := trace.StartSpan(ctx, "statemanager.CallWithGas")
defer span.End()
Expand Down
4 changes: 2 additions & 2 deletions cli/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -1355,7 +1355,7 @@ func ComputeStateHTMLTempl(w io.Writer, ts *types.TipSet, o *api.ComputeStateOut
"IsSlow": isSlow,
"IsVerySlow": isVerySlow,
"IntExit": func(i exitcode.ExitCode) int64 { return int64(i) },
"SumGas": sumGas,
"SumGas": SumGas,
"CodeStr": codeStr,
"Call": call,
"PrintTiming": func() bool { return printTiming },
Expand Down Expand Up @@ -1423,7 +1423,7 @@ func isVerySlow(t time.Duration) bool {
return t > 50*time.Millisecond
}

func sumGas(changes []*types.GasTrace) types.GasTrace {
func SumGas(changes []*types.GasTrace) types.GasTrace {
geoff-vball marked this conversation as resolved.
Show resolved Hide resolved
var out types.GasTrace
for _, gc := range changes {
out.TotalGas += gc.TotalGas
Expand Down
178 changes: 178 additions & 0 deletions cmd/lotus-shed/gas-estimation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package main

import (
"context"
"fmt"
"io"
"os"
"strconv"
"text/tabwriter"

"github.com/ipfs/go-cid"
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"

"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/network"

"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/beacon"
"github.com/filecoin-project/lotus/chain/beacon/drand"
"github.com/filecoin-project/lotus/chain/consensus/filcns"
"github.com/filecoin-project/lotus/chain/stmgr"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/vm"
lcli "github.com/filecoin-project/lotus/cli"
"github.com/filecoin-project/lotus/node/repo"
"github.com/filecoin-project/lotus/storage/sealer/ffiwrapper"
)

// USAGE: Sync a node, then call migrate-nv17 on some old state. Pass in the cid of the migrated state root,
// the epoch you migrated at, the network version you migrated to, and a message hash. You will be able to replay any
geoff-vball marked this conversation as resolved.
Show resolved Hide resolved
// message from between the migration epoch, and where your node originally synced to. Note: You may run into issues
// with nonces, or state that changed between the epoch you migrated at, and when the message was originally processed.
// This can be avoided by replaying messages from close to the migration epoch, or circumvented by using a custom
// FVM bundle.
var gasTraceCmd = &cli.Command{
Name: "trace-gas",
Description: "replay a message on the specified stateRoot and network version to get an execution trace",
ArgsUsage: "[migratedStateRootCid migrationEpoch networkVersion messageHash]",
geoff-vball marked this conversation as resolved.
Show resolved Hide resolved
Flags: []cli.Flag{
&cli.StringFlag{
Name: "repo",
Value: "~/.lotus",
},
},
Action: func(cctx *cli.Context) error {
ctx := context.TODO()

if cctx.NArg() != 4 {
return lcli.IncorrectNumArgs(cctx)
}

stateRootCid, err := cid.Decode(cctx.Args().Get(0))
if err != nil {
return fmt.Errorf("failed to parse input: %w", err)
}

epoch, err := strconv.ParseInt(cctx.Args().Get(1), 10, 64)
if err != nil {
return fmt.Errorf("failed to parse input: %w", err)
}

nv, err := strconv.ParseInt(cctx.Args().Get(2), 10, 32)
if err != nil {
return fmt.Errorf("failed to parse input: %w", err)
}

messageCid, err := cid.Decode(cctx.Args().Get(3))
if err != nil {
return fmt.Errorf("failed to parse input: %w", err)
}

fsrepo, err := repo.NewFS(cctx.String("repo"))
if err != nil {
return err
}

lkrepo, err := fsrepo.Lock(repo.FullNode)
if err != nil {
return err
}

defer lkrepo.Close() //nolint:errcheck

bs, err := lkrepo.Blockstore(ctx, repo.UniversalBlockstore)
if err != nil {
return fmt.Errorf("failed to open blockstore: %w", err)
}

defer func() {
if c, ok := bs.(io.Closer); ok {
if err := c.Close(); err != nil {
log.Warnf("failed to close blockstore: %s", err)
}
}
}()

mds, err := lkrepo.Datastore(context.Background(), "/metadata")
if err != nil {
return err
}

dcs := build.DrandConfigSchedule()
shd := beacon.Schedule{}
for _, dc := range dcs {
bc, err := drand.NewDrandBeacon(1598306400, build.BlockDelaySecs, nil, dc.Config)
geoff-vball marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return xerrors.Errorf("creating drand beacon: %w", err)
}
shd = append(shd, beacon.BeaconPoint{Start: dc.Start, Beacon: bc})
}
cs := store.NewChainStore(bs, bs, mds, filcns.Weight, nil)
defer cs.Close() //nolint:errcheck

sm, err := stmgr.NewStateManager(cs, filcns.NewTipSetExecutor(), vm.Syscalls(ffiwrapper.ProofVerifier), filcns.DefaultUpgradeSchedule(), shd)
if err != nil {
return err
}

msg, err := cs.GetMessage(ctx, messageCid)
if err != nil {
return err
}

// Set to block limit so message will not run out of gas
msg.GasLimit = 10_000_000_000
geoff-vball marked this conversation as resolved.
Show resolved Hide resolved

err = cs.Load(ctx)
if err != nil {
return err
}

executionTs, err := cs.GetTipsetByHeight(ctx, abi.ChainEpoch(epoch), nil, false)
if err != nil {
return err
}

tw := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', tabwriter.AlignRight)
res, err := sm.CallAtStateAndVersion(ctx, msg, executionTs, stateRootCid, network.Version(nv))
Fixed Show fixed Hide fixed
if err != nil {
return err
}
fmt.Println("Total gas used ", res.MsgRct.GasUsed)
printInternalExecutions(0, []types.ExecutionTrace{res.ExecutionTrace}, tw)

return tw.Flush()
},
}

func printInternalExecutions(depth int, trace []types.ExecutionTrace, tw *tabwriter.Writer) {
if depth == 0 {
_, _ = fmt.Fprintf(tw, "Depth\tFrom\tTo\tMethod\tTotalGas\tComputeGas\tStorageGas\t\tExitCode\n")
}
for _, im := range trace {
sumGas := lcli.SumGas(im.GasCharges)
_, _ = fmt.Fprintf(tw, "%d\t%s\t%s\t%d\t%d\t%d\t%d\t\t%d\n", depth, truncateString(im.Msg.From.String(), 10), truncateString(im.Msg.To.String(), 10), im.Msg.Method, sumGas.TotalGas, sumGas.ComputeGas, sumGas.StorageGas, im.MsgRct.ExitCode)
printInternalExecutions(depth+1, im.Subcalls, tw)
}
}

func truncateString(str string, length int) string {
if len(str) <= length {
return str
}

truncated := ""
count := 0
for _, char := range str {
truncated += string(char)
count++
if count >= length {
break
}
}
truncated += "..."
return truncated
}
1 change: 1 addition & 0 deletions cmd/lotus-shed/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ func main() {
msigCmd,
fip36PollCmd,
invariantsCmd,
gasTraceCmd,
}

app := &cli.App{
Expand Down
Loading