diff --git a/chain/actors/builtin/registry.go b/chain/actors/builtin/registry.go index 15b2809d0..622bf4185 100644 --- a/chain/actors/builtin/registry.go +++ b/chain/actors/builtin/registry.go @@ -1,9 +1,14 @@ package builtin import ( + "reflect" + "runtime" + "strings" + "github.com/ipfs/go-cid" actorstypes "github.com/filecoin-project/go-state-types/actors" + "github.com/filecoin-project/go-state-types/builtin" account8 "github.com/filecoin-project/go-state-types/builtin/v8/account" cron8 "github.com/filecoin-project/go-state-types/builtin/v8/cron" _init8 "github.com/filecoin-project/go-state-types/builtin/v8/init" @@ -36,14 +41,14 @@ import ( type RegistryEntry struct { state cbor.Er code cid.Cid - methods map[uint64]interface{} + methods map[uint64]builtin.MethodMeta } func (r RegistryEntry) State() cbor.Er { return r.state } -func (r RegistryEntry) Exports() map[uint64]interface{} { +func (r RegistryEntry) Exports() map[uint64]builtin.MethodMeta { return r.methods } @@ -55,9 +60,11 @@ func MakeRegistryLegacy(actors []rtt.VMActor) []RegistryEntry { registry := make([]RegistryEntry, 0) for _, actor := range actors { - methodMap := make(map[uint64]interface{}) + methodMap := make(map[uint64]builtin.MethodMeta) for methodNum, method := range actor.Exports() { - methodMap[uint64(methodNum)] = method + if method != nil { + methodMap[uint64(methodNum)] = makeMethodMeta(method) + } } registry = append(registry, RegistryEntry{ code: actor.Code(), @@ -69,6 +76,20 @@ func MakeRegistryLegacy(actors []rtt.VMActor) []RegistryEntry { return registry } +func makeMethodMeta(method interface{}) builtin.MethodMeta { + ev := reflect.ValueOf(method) + // Extract the method names using reflection. These + // method names always match the field names in the + // `builtin.Method*` structs (tested in the specs-actors + // tests). + fnName := runtime.FuncForPC(ev.Pointer()).Name() + fnName = strings.TrimSuffix(fnName[strings.LastIndexByte(fnName, '.')+1:], "-fm") + return builtin.MethodMeta{ + Name: fnName, + Method: method, + } +} + func MakeRegistry(av actorstypes.Version) []RegistryEntry { if av < actorstypes.Version8 { panic("expected version v8 and up only, use specs-actors for v0-7") diff --git a/chain/actors/builtin/registry.go.template b/chain/actors/builtin/registry.go.template index 59aa3aa0f..39211c476 100644 --- a/chain/actors/builtin/registry.go.template +++ b/chain/actors/builtin/registry.go.template @@ -3,6 +3,11 @@ package builtin import ( actorstypes "github.com/filecoin-project/go-state-types/actors" "github.com/ipfs/go-cid" + "reflect" + "runtime" + "strings" + + "github.com/filecoin-project/go-state-types/builtin" {{range .versions}} {{if (ge . 8)}} account{{.}} "github.com/filecoin-project/go-state-types/builtin/v{{.}}/account" @@ -29,14 +34,14 @@ import ( type RegistryEntry struct { state cbor.Er code cid.Cid - methods map[uint64]interface{} + methods map[uint64]builtin.MethodMeta } func (r RegistryEntry) State() cbor.Er { return r.state } -func (r RegistryEntry) Exports() map[uint64]interface{} { +func (r RegistryEntry) Exports() map[uint64]builtin.MethodMeta { return r.methods } @@ -48,10 +53,12 @@ func MakeRegistryLegacy(actors []rtt.VMActor) []RegistryEntry { registry := make([]RegistryEntry, 0) for _, actor := range actors { - methodMap := make(map[uint64]interface{}) + methodMap := make(map[uint64]builtin.MethodMeta) for methodNum, method := range actor.Exports() { - methodMap[uint64(methodNum)] = method - } + if method != nil { + methodMap[uint64(methodNum)] = makeMethodMeta(method) + } + } registry = append(registry, RegistryEntry{ code: actor.Code(), methods: methodMap, @@ -62,6 +69,20 @@ func MakeRegistryLegacy(actors []rtt.VMActor) []RegistryEntry { return registry } +func makeMethodMeta(method interface{}) builtin.MethodMeta { + ev := reflect.ValueOf(method) + // Extract the method names using reflection. These + // method names always match the field names in the + // `builtin.Method*` structs (tested in the specs-actors + // tests). + fnName := runtime.FuncForPC(ev.Pointer()).Name() + fnName = strings.TrimSuffix(fnName[strings.LastIndexByte(fnName, '.')+1:], "-fm") + return builtin.MethodMeta{ + Name: fnName, + Method: method, + } +} + func MakeRegistry(av actorstypes.Version) []RegistryEntry { if av < actorstypes.Version8 { panic("expected version v8 and up only, use specs-actors for v0-7") diff --git a/chain/stmgr/utils.go b/chain/stmgr/utils.go index 03ade3bd2..a466029ce 100644 --- a/chain/stmgr/utils.go +++ b/chain/stmgr/utils.go @@ -38,7 +38,6 @@ func GetReturnType(ctx context.Context, sm *StateManager, to address.Address, me return nil, fmt.Errorf("unknown method %d for actor %s", method, act.Code) } - fmt.Println("found ", m.Ret, " and ", m.Params, " for ", m.Num) return reflect.New(m.Ret.Elem()).Interface().(cbg.CBORUnmarshaler), nil } diff --git a/chain/vm/invoker.go b/chain/vm/invoker.go index f8d817ff2..bcd81995f 100644 --- a/chain/vm/invoker.go +++ b/chain/vm/invoker.go @@ -5,7 +5,6 @@ import ( "encoding/hex" "fmt" "reflect" - "strconv" "github.com/ipfs/go-cid" cbg "github.com/whyrusleeping/cbor-gen" @@ -13,6 +12,7 @@ import ( "github.com/filecoin-project/go-state-types/abi" actorstypes "github.com/filecoin-project/go-state-types/actors" + builtinst "github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/go-state-types/exitcode" "github.com/filecoin-project/go-state-types/network" vmr "github.com/filecoin-project/specs-actors/v7/actors/runtime" @@ -24,7 +24,7 @@ import ( ) type MethodMeta struct { - Num string + Name string Params reflect.Type Ret reflect.Type @@ -90,10 +90,15 @@ func (ar *ActorRegistry) Register(av actorstypes.Version, pred ActorPredicate, v pred = func(vmr.Runtime, cid.Cid) error { return nil } } for _, a := range vmactors { - // register in the `actors` map (for the invoker) - code, err := ar.transform(a) - if err != nil { - panic(xerrors.Errorf("%s: %w", string(a.Code().Hash()), err)) + + var code nativeCode + var err error + if av <= actorstypes.Version7 { + // register in the `actors` map (for the invoker) + code, err = ar.transform(a) + if err != nil { + panic(xerrors.Errorf("%s: %w", string(a.Code().Hash()), err)) + } } ai := &actorInfo{ @@ -123,7 +128,7 @@ func (ar *ActorRegistry) Register(av actorstypes.Version, pred ActorPredicate, v // Explicitly add send, it's special. methods[builtin.MethodSend] = MethodMeta{ - Num: "0", + Name: "Send", Params: reflect.TypeOf(new(abi.EmptyValue)), Ret: reflect.TypeOf(new(abi.EmptyValue)), } @@ -131,18 +136,27 @@ func (ar *ActorRegistry) Register(av actorstypes.Version, pred ActorPredicate, v // Iterate over exported methods. Some of these _may_ be nil and // must be skipped. for number, export := range exports { - if export == nil { + if export.Method == nil { continue } - ev := reflect.ValueOf(export) + ev := reflect.ValueOf(export.Method) et := ev.Type() - methods[abi.MethodNum(number)] = MethodMeta{ - Num: strconv.Itoa(int(number)), - Params: et.In(1), - Ret: et.Out(0), + mm := MethodMeta{ + Name: export.Name, + Ret: et.Out(0), } + + if av <= actorstypes.Version7 { + // methods exported from specs-actors have the runtime as the first param, so we want et.In(1) + mm.Params = et.In(1) + } else { + // methods exported from go-state-types do not, so we want et.In(0) + mm.Params = et.In(0) + } + + methods[abi.MethodNum(number)] = mm } if realCode.Defined() { ar.Methods[realCode] = methods @@ -171,15 +185,16 @@ func (ar *ActorRegistry) Create(codeCid cid.Cid, rt vmr.Runtime) (*types.Actor, } type invokee interface { - Exports() map[uint64]interface{} + Exports() map[uint64]builtinst.MethodMeta } func (*ActorRegistry) transform(instance invokee) (nativeCode, error) { itype := reflect.TypeOf(instance) exports := instance.Exports() runtimeType := reflect.TypeOf((*vmr.Runtime)(nil)).Elem() - for i, m := range exports { + for i, e := range exports { i := i + m := e.Method newErr := func(format string, args ...interface{}) error { str := fmt.Sprintf(format, args...) return fmt.Errorf("transform(%s) export(%d): %s", itype.Name(), i, str) @@ -215,7 +230,8 @@ func (*ActorRegistry) transform(instance invokee) (nativeCode, error) { } } code := make(nativeCode, len(exports)) - for id, m := range exports { + for id, e := range exports { + m := e.Method if m == nil { continue } diff --git a/chain/vm/invoker_test.go b/chain/vm/invoker_test.go index 340f044d8..d3e6dcd7f 100644 --- a/chain/vm/invoker_test.go +++ b/chain/vm/invoker_test.go @@ -6,6 +6,7 @@ import ( "io" "testing" + cid "github.com/ipfs/go-cid" cbor "github.com/ipfs/go-ipld-cbor" "github.com/stretchr/testify/assert" cbg "github.com/whyrusleeping/cbor-gen" @@ -13,15 +14,28 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" + cbor2 "github.com/filecoin-project/go-state-types/cbor" "github.com/filecoin-project/go-state-types/exitcode" "github.com/filecoin-project/go-state-types/network" + "github.com/filecoin-project/go-state-types/rt" runtime2 "github.com/filecoin-project/specs-actors/v2/actors/runtime" "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/actors/aerrors" + "github.com/filecoin-project/lotus/chain/actors/builtin" ) type basicContract struct{} + +func (b basicContract) Code() cid.Cid { + return cid.Undef +} + +func (b basicContract) State() cbor2.Er { + // works well enough as a dummy state + return new(basicParams) +} + type basicParams struct { B byte } @@ -49,19 +63,19 @@ func init() { cbor.RegisterCborType(basicParams{}) } -func (b basicContract) Exports() map[uint64]interface{} { - return map[uint64]interface{}{ - 0: b.InvokeSomething0, - 1: b.BadParam, - 2: nil, - 3: nil, - 4: nil, - 5: nil, - 6: nil, - 7: nil, - 8: nil, - 9: nil, - 10: b.InvokeSomething10, +func (b basicContract) Exports() []interface{} { + return []interface{}{ + b.InvokeSomething0, + b.BadParam, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + b.InvokeSomething10, } } @@ -107,7 +121,8 @@ func (*basicRtMessage) ValueReceived() abi.TokenAmount { func TestInvokerBasic(t *testing.T) { //stm: @INVOKER_TRANSFORM_001 inv := ActorRegistry{} - code, err := inv.transform(basicContract{}) + registry := builtin.MakeRegistryLegacy([]rt.VMActor{basicContract{}}) + code, err := inv.transform(registry[0]) assert.NoError(t, err) { diff --git a/cli/chain.go b/cli/chain.go index 814aebeed..6ecb3f6b1 100644 --- a/cli/chain.go +++ b/cli/chain.go @@ -494,8 +494,8 @@ var ChainInspectUsage = &cli.Command{ mm := filcns.NewActorRegistry().Methods[code][m.Message.Method] // TODO: use remote map - byMethod[mm.Num] += m.Message.GasLimit - byMethodC[mm.Num]++ + byMethod[mm.Name] += m.Message.GasLimit + byMethodC[mm.Name]++ } type keyGasPair struct { diff --git a/cli/chain_test.go b/cli/chain_test.go index 80af1a6e7..9fd46724e 100644 --- a/cli/chain_test.go +++ b/cli/chain_test.go @@ -327,8 +327,7 @@ func TestInspectUsage(t *testing.T) { // check for gas by sender assert.Contains(t, out, "By Sender") // check for gas by method - methodStr := fmt.Sprintf("By Method:\n%d", builtin.MethodSend) - assert.Contains(t, out, methodStr) + assert.Contains(t, out, "By Method:\nSend") }) } diff --git a/cli/multisig.go b/cli/multisig.go index 1dd7d01b8..38923a04f 100644 --- a/cli/multisig.go +++ b/cli/multisig.go @@ -341,7 +341,7 @@ var msigInspectCmd = &cli.Command{ paramStr = string(b) } - 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.Num, tx.Method, paramStr) + 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) } } if err := w.Flush(); err != nil { diff --git a/cli/state.go b/cli/state.go index c88df8da2..d5ce46f4c 100644 --- a/cli/state.go +++ b/cli/state.go @@ -1384,7 +1384,7 @@ func codeStr(c cid.Cid) string { } func getMethod(code cid.Cid, method abi.MethodNum) string { - return filcns.NewActorRegistry().Methods[code][method].Num // todo: use remote + return filcns.NewActorRegistry().Methods[code][method].Name // todo: use remote } func toFil(f types.BigInt) types.FIL { diff --git a/cmd/lotus-shed/msg.go b/cmd/lotus-shed/msg.go index 847b93d9f..9096c6a5f 100644 --- a/cmd/lotus-shed/msg.go +++ b/cmd/lotus-shed/msg.go @@ -141,7 +141,7 @@ func printMessage(cctx *cli.Context, msg *types.Message) error { return nil } - fmt.Println("Method:", filcns.NewActorRegistry().Methods[toact.Code][msg.Method].Num) // todo use remote + fmt.Println("Method:", filcns.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 diff --git a/cmd/lotus-wallet/interactive.go b/cmd/lotus-wallet/interactive.go index 9770961c0..40c3f8922 100644 --- a/cmd/lotus-wallet/interactive.go +++ b/cmd/lotus-wallet/interactive.go @@ -103,7 +103,7 @@ func (c *InteractiveWallet) WalletSign(ctx context.Context, k address.Address, m return xerrors.Errorf("looking up dest actor: %w", err) } - fmt.Println("Method:", filcns.NewActorRegistry().Methods[toact.Code][cmsg.Method].Num) + fmt.Println("Method:", filcns.NewActorRegistry().Methods[toact.Code][cmsg.Method].Name) p, err := lcli.JsonParams(toact.Code, cmsg.Method, cmsg.Params) if err != nil { return err @@ -125,7 +125,7 @@ func (c *InteractiveWallet) WalletSign(ctx context.Context, k address.Address, m return xerrors.Errorf("looking up msig dest actor: %w", err) } - fmt.Println("\tMultiSig Proposal Method:", filcns.NewActorRegistry().Methods[toact.Code][mp.Method].Num) // todo use remote + fmt.Println("\tMultiSig Proposal Method:", filcns.NewActorRegistry().Methods[toact.Code][mp.Method].Name) // todo use remote p, err := lcli.JsonParams(toact.Code, mp.Method, mp.Params) if err != nil { return err diff --git a/cmd/tvx/extract_many.go b/cmd/tvx/extract_many.go index 07bbc8df4..bc5ad2a30 100644 --- a/cmd/tvx/extract_many.go +++ b/cmd/tvx/extract_many.go @@ -164,7 +164,7 @@ func runExtractMany(c *cli.Context) error { } else if methodnum >= len(m) { return fmt.Errorf("unrecognized method number for actor %s: %d", actorcode, methodnum) } else { - methodname = m[abi.MethodNum(methodnum)].Num + methodname = m[abi.MethodNum(methodnum)].Name } // exitcode string representations are of kind ErrType(0); strip out diff --git a/go.mod b/go.mod index b88d40e21..7cf364cc0 100644 --- a/go.mod +++ b/go.mod @@ -42,7 +42,7 @@ require ( github.com/filecoin-project/go-legs v0.4.4 github.com/filecoin-project/go-padreader v0.0.1 github.com/filecoin-project/go-paramfetch v0.0.4 - github.com/filecoin-project/go-state-types v0.9.1 + github.com/filecoin-project/go-state-types v0.9.2 github.com/filecoin-project/go-statemachine v1.0.2 github.com/filecoin-project/go-statestore v0.2.0 github.com/filecoin-project/go-storedcounter v0.1.0 diff --git a/go.sum b/go.sum index 774feb5b0..24ace049c 100644 --- a/go.sum +++ b/go.sum @@ -343,8 +343,8 @@ github.com/filecoin-project/go-state-types v0.1.0/go.mod h1:ezYnPf0bNkTsDibL/psS github.com/filecoin-project/go-state-types v0.1.6/go.mod h1:UwGVoMsULoCK+bWjEdd/xLCvLAQFBC7EDT477SKml+Q= github.com/filecoin-project/go-state-types v0.1.8/go.mod h1:UwGVoMsULoCK+bWjEdd/xLCvLAQFBC7EDT477SKml+Q= github.com/filecoin-project/go-state-types v0.1.10/go.mod h1:UwGVoMsULoCK+bWjEdd/xLCvLAQFBC7EDT477SKml+Q= -github.com/filecoin-project/go-state-types v0.9.1 h1:Dr++Hy+tm8RC5jLQBPfFIvThPCa6uEJ4NwlCWh3V85Q= -github.com/filecoin-project/go-state-types v0.9.1/go.mod h1:+HCZifUV+e8TlQkgll22Ucuiq8OrVJkK+4Kh4u75iiw= +github.com/filecoin-project/go-state-types v0.9.2 h1:zbhib/addhqVihN7yZPkBMvkpS6v5PQFtBllDIxdUS4= +github.com/filecoin-project/go-state-types v0.9.2/go.mod h1:+HCZifUV+e8TlQkgll22Ucuiq8OrVJkK+4Kh4u75iiw= github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe/go.mod h1:FGwQgZAt2Gh5mjlwJUlVB62JeYdo+if0xWxSEfBD9ig= github.com/filecoin-project/go-statemachine v1.0.2 h1:421SSWBk8GIoCoWYYTE/d+qCWccgmRH0uXotXRDjUbc= github.com/filecoin-project/go-statemachine v1.0.2/go.mod h1:jZdXXiHa61n4NmgWFG4w8tnqgvZVHYbJ3yW7+y8bF54=