Invoker: Use MethodMeta from go-state-types

This commit is contained in:
Aayush 2022-10-19 13:11:44 -04:00
parent 8190658fe9
commit 13b59c9c23
14 changed files with 124 additions and 53 deletions

View File

@ -1,9 +1,14 @@
package builtin package builtin
import ( import (
"reflect"
"runtime"
"strings"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
actorstypes "github.com/filecoin-project/go-state-types/actors" 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" account8 "github.com/filecoin-project/go-state-types/builtin/v8/account"
cron8 "github.com/filecoin-project/go-state-types/builtin/v8/cron" cron8 "github.com/filecoin-project/go-state-types/builtin/v8/cron"
_init8 "github.com/filecoin-project/go-state-types/builtin/v8/init" _init8 "github.com/filecoin-project/go-state-types/builtin/v8/init"
@ -36,14 +41,14 @@ import (
type RegistryEntry struct { type RegistryEntry struct {
state cbor.Er state cbor.Er
code cid.Cid code cid.Cid
methods map[uint64]interface{} methods map[uint64]builtin.MethodMeta
} }
func (r RegistryEntry) State() cbor.Er { func (r RegistryEntry) State() cbor.Er {
return r.state return r.state
} }
func (r RegistryEntry) Exports() map[uint64]interface{} { func (r RegistryEntry) Exports() map[uint64]builtin.MethodMeta {
return r.methods return r.methods
} }
@ -55,9 +60,11 @@ func MakeRegistryLegacy(actors []rtt.VMActor) []RegistryEntry {
registry := make([]RegistryEntry, 0) registry := make([]RegistryEntry, 0)
for _, actor := range actors { for _, actor := range actors {
methodMap := make(map[uint64]interface{}) methodMap := make(map[uint64]builtin.MethodMeta)
for methodNum, method := range actor.Exports() { for methodNum, method := range actor.Exports() {
methodMap[uint64(methodNum)] = method if method != nil {
methodMap[uint64(methodNum)] = makeMethodMeta(method)
}
} }
registry = append(registry, RegistryEntry{ registry = append(registry, RegistryEntry{
code: actor.Code(), code: actor.Code(),
@ -69,6 +76,20 @@ func MakeRegistryLegacy(actors []rtt.VMActor) []RegistryEntry {
return registry 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 { func MakeRegistry(av actorstypes.Version) []RegistryEntry {
if av < actorstypes.Version8 { if av < actorstypes.Version8 {
panic("expected version v8 and up only, use specs-actors for v0-7") panic("expected version v8 and up only, use specs-actors for v0-7")

View File

@ -3,6 +3,11 @@ package builtin
import ( import (
actorstypes "github.com/filecoin-project/go-state-types/actors" actorstypes "github.com/filecoin-project/go-state-types/actors"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
"reflect"
"runtime"
"strings"
"github.com/filecoin-project/go-state-types/builtin"
{{range .versions}} {{range .versions}}
{{if (ge . 8)}} {{if (ge . 8)}}
account{{.}} "github.com/filecoin-project/go-state-types/builtin/v{{.}}/account" account{{.}} "github.com/filecoin-project/go-state-types/builtin/v{{.}}/account"
@ -29,14 +34,14 @@ import (
type RegistryEntry struct { type RegistryEntry struct {
state cbor.Er state cbor.Er
code cid.Cid code cid.Cid
methods map[uint64]interface{} methods map[uint64]builtin.MethodMeta
} }
func (r RegistryEntry) State() cbor.Er { func (r RegistryEntry) State() cbor.Er {
return r.state return r.state
} }
func (r RegistryEntry) Exports() map[uint64]interface{} { func (r RegistryEntry) Exports() map[uint64]builtin.MethodMeta {
return r.methods return r.methods
} }
@ -48,9 +53,11 @@ func MakeRegistryLegacy(actors []rtt.VMActor) []RegistryEntry {
registry := make([]RegistryEntry, 0) registry := make([]RegistryEntry, 0)
for _, actor := range actors { for _, actor := range actors {
methodMap := make(map[uint64]interface{}) methodMap := make(map[uint64]builtin.MethodMeta)
for methodNum, method := range actor.Exports() { for methodNum, method := range actor.Exports() {
methodMap[uint64(methodNum)] = method if method != nil {
methodMap[uint64(methodNum)] = makeMethodMeta(method)
}
} }
registry = append(registry, RegistryEntry{ registry = append(registry, RegistryEntry{
code: actor.Code(), code: actor.Code(),
@ -62,6 +69,20 @@ func MakeRegistryLegacy(actors []rtt.VMActor) []RegistryEntry {
return registry 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 { func MakeRegistry(av actorstypes.Version) []RegistryEntry {
if av < actorstypes.Version8 { if av < actorstypes.Version8 {
panic("expected version v8 and up only, use specs-actors for v0-7") panic("expected version v8 and up only, use specs-actors for v0-7")

View File

@ -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) 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 return reflect.New(m.Ret.Elem()).Interface().(cbg.CBORUnmarshaler), nil
} }

View File

@ -5,7 +5,6 @@ import (
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"reflect" "reflect"
"strconv"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
cbg "github.com/whyrusleeping/cbor-gen" cbg "github.com/whyrusleeping/cbor-gen"
@ -13,6 +12,7 @@ import (
"github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/abi"
actorstypes "github.com/filecoin-project/go-state-types/actors" 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/exitcode"
"github.com/filecoin-project/go-state-types/network" "github.com/filecoin-project/go-state-types/network"
vmr "github.com/filecoin-project/specs-actors/v7/actors/runtime" vmr "github.com/filecoin-project/specs-actors/v7/actors/runtime"
@ -24,7 +24,7 @@ import (
) )
type MethodMeta struct { type MethodMeta struct {
Num string Name string
Params reflect.Type Params reflect.Type
Ret reflect.Type Ret reflect.Type
@ -90,11 +90,16 @@ func (ar *ActorRegistry) Register(av actorstypes.Version, pred ActorPredicate, v
pred = func(vmr.Runtime, cid.Cid) error { return nil } pred = func(vmr.Runtime, cid.Cid) error { return nil }
} }
for _, a := range vmactors { for _, a := range vmactors {
var code nativeCode
var err error
if av <= actorstypes.Version7 {
// register in the `actors` map (for the invoker) // register in the `actors` map (for the invoker)
code, err := ar.transform(a) code, err = ar.transform(a)
if err != nil { if err != nil {
panic(xerrors.Errorf("%s: %w", string(a.Code().Hash()), err)) panic(xerrors.Errorf("%s: %w", string(a.Code().Hash()), err))
} }
}
ai := &actorInfo{ ai := &actorInfo{
methods: code, methods: code,
@ -123,7 +128,7 @@ func (ar *ActorRegistry) Register(av actorstypes.Version, pred ActorPredicate, v
// Explicitly add send, it's special. // Explicitly add send, it's special.
methods[builtin.MethodSend] = MethodMeta{ methods[builtin.MethodSend] = MethodMeta{
Num: "0", Name: "Send",
Params: reflect.TypeOf(new(abi.EmptyValue)), Params: reflect.TypeOf(new(abi.EmptyValue)),
Ret: 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 // Iterate over exported methods. Some of these _may_ be nil and
// must be skipped. // must be skipped.
for number, export := range exports { for number, export := range exports {
if export == nil { if export.Method == nil {
continue continue
} }
ev := reflect.ValueOf(export) ev := reflect.ValueOf(export.Method)
et := ev.Type() et := ev.Type()
methods[abi.MethodNum(number)] = MethodMeta{ mm := MethodMeta{
Num: strconv.Itoa(int(number)), Name: export.Name,
Params: et.In(1),
Ret: et.Out(0), 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() { if realCode.Defined() {
ar.Methods[realCode] = methods ar.Methods[realCode] = methods
@ -171,15 +185,16 @@ func (ar *ActorRegistry) Create(codeCid cid.Cid, rt vmr.Runtime) (*types.Actor,
} }
type invokee interface { type invokee interface {
Exports() map[uint64]interface{} Exports() map[uint64]builtinst.MethodMeta
} }
func (*ActorRegistry) transform(instance invokee) (nativeCode, error) { func (*ActorRegistry) transform(instance invokee) (nativeCode, error) {
itype := reflect.TypeOf(instance) itype := reflect.TypeOf(instance)
exports := instance.Exports() exports := instance.Exports()
runtimeType := reflect.TypeOf((*vmr.Runtime)(nil)).Elem() runtimeType := reflect.TypeOf((*vmr.Runtime)(nil)).Elem()
for i, m := range exports { for i, e := range exports {
i := i i := i
m := e.Method
newErr := func(format string, args ...interface{}) error { newErr := func(format string, args ...interface{}) error {
str := fmt.Sprintf(format, args...) str := fmt.Sprintf(format, args...)
return fmt.Errorf("transform(%s) export(%d): %s", itype.Name(), i, str) 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)) code := make(nativeCode, len(exports))
for id, m := range exports { for id, e := range exports {
m := e.Method
if m == nil { if m == nil {
continue continue
} }

View File

@ -6,6 +6,7 @@ import (
"io" "io"
"testing" "testing"
cid "github.com/ipfs/go-cid"
cbor "github.com/ipfs/go-ipld-cbor" cbor "github.com/ipfs/go-ipld-cbor"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
cbg "github.com/whyrusleeping/cbor-gen" cbg "github.com/whyrusleeping/cbor-gen"
@ -13,15 +14,28 @@ import (
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big" "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/exitcode"
"github.com/filecoin-project/go-state-types/network" "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" runtime2 "github.com/filecoin-project/specs-actors/v2/actors/runtime"
"github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/actors/aerrors" "github.com/filecoin-project/lotus/chain/actors/aerrors"
"github.com/filecoin-project/lotus/chain/actors/builtin"
) )
type basicContract struct{} 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 { type basicParams struct {
B byte B byte
} }
@ -49,19 +63,19 @@ func init() {
cbor.RegisterCborType(basicParams{}) cbor.RegisterCborType(basicParams{})
} }
func (b basicContract) Exports() map[uint64]interface{} { func (b basicContract) Exports() []interface{} {
return map[uint64]interface{}{ return []interface{}{
0: b.InvokeSomething0, b.InvokeSomething0,
1: b.BadParam, b.BadParam,
2: nil, nil,
3: nil, nil,
4: nil, nil,
5: nil, nil,
6: nil, nil,
7: nil, nil,
8: nil, nil,
9: nil, nil,
10: b.InvokeSomething10, b.InvokeSomething10,
} }
} }
@ -107,7 +121,8 @@ func (*basicRtMessage) ValueReceived() abi.TokenAmount {
func TestInvokerBasic(t *testing.T) { func TestInvokerBasic(t *testing.T) {
//stm: @INVOKER_TRANSFORM_001 //stm: @INVOKER_TRANSFORM_001
inv := ActorRegistry{} inv := ActorRegistry{}
code, err := inv.transform(basicContract{}) registry := builtin.MakeRegistryLegacy([]rt.VMActor{basicContract{}})
code, err := inv.transform(registry[0])
assert.NoError(t, err) assert.NoError(t, err)
{ {

View File

@ -494,8 +494,8 @@ var ChainInspectUsage = &cli.Command{
mm := filcns.NewActorRegistry().Methods[code][m.Message.Method] // TODO: use remote map mm := filcns.NewActorRegistry().Methods[code][m.Message.Method] // TODO: use remote map
byMethod[mm.Num] += m.Message.GasLimit byMethod[mm.Name] += m.Message.GasLimit
byMethodC[mm.Num]++ byMethodC[mm.Name]++
} }
type keyGasPair struct { type keyGasPair struct {

View File

@ -327,8 +327,7 @@ func TestInspectUsage(t *testing.T) {
// check for gas by sender // check for gas by sender
assert.Contains(t, out, "By Sender") assert.Contains(t, out, "By Sender")
// check for gas by method // check for gas by method
methodStr := fmt.Sprintf("By Method:\n%d", builtin.MethodSend) assert.Contains(t, out, "By Method:\nSend")
assert.Contains(t, out, methodStr)
}) })
} }

View File

@ -341,7 +341,7 @@ var msigInspectCmd = &cli.Command{
paramStr = string(b) 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 { if err := w.Flush(); err != nil {

View File

@ -1384,7 +1384,7 @@ func codeStr(c cid.Cid) string {
} }
func getMethod(code cid.Cid, method abi.MethodNum) 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 { func toFil(f types.BigInt) types.FIL {

View File

@ -141,7 +141,7 @@ func printMessage(cctx *cli.Context, msg *types.Message) error {
return nil 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) p, err := lcli.JsonParams(toact.Code, msg.Method, msg.Params)
if err != nil { if err != nil {
return err return err

View File

@ -103,7 +103,7 @@ func (c *InteractiveWallet) WalletSign(ctx context.Context, k address.Address, m
return xerrors.Errorf("looking up dest actor: %w", err) 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) p, err := lcli.JsonParams(toact.Code, cmsg.Method, cmsg.Params)
if err != nil { if err != nil {
return err 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) 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) p, err := lcli.JsonParams(toact.Code, mp.Method, mp.Params)
if err != nil { if err != nil {
return err return err

View File

@ -164,7 +164,7 @@ func runExtractMany(c *cli.Context) error {
} else if methodnum >= len(m) { } else if methodnum >= len(m) {
return fmt.Errorf("unrecognized method number for actor %s: %d", actorcode, methodnum) return fmt.Errorf("unrecognized method number for actor %s: %d", actorcode, methodnum)
} else { } else {
methodname = m[abi.MethodNum(methodnum)].Num methodname = m[abi.MethodNum(methodnum)].Name
} }
// exitcode string representations are of kind ErrType(0); strip out // exitcode string representations are of kind ErrType(0); strip out

2
go.mod
View File

@ -42,7 +42,7 @@ require (
github.com/filecoin-project/go-legs v0.4.4 github.com/filecoin-project/go-legs v0.4.4
github.com/filecoin-project/go-padreader v0.0.1 github.com/filecoin-project/go-padreader v0.0.1
github.com/filecoin-project/go-paramfetch v0.0.4 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-statemachine v1.0.2
github.com/filecoin-project/go-statestore v0.2.0 github.com/filecoin-project/go-statestore v0.2.0
github.com/filecoin-project/go-storedcounter v0.1.0 github.com/filecoin-project/go-storedcounter v0.1.0

4
go.sum
View File

@ -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.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.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.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.2 h1:zbhib/addhqVihN7yZPkBMvkpS6v5PQFtBllDIxdUS4=
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/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 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 h1:421SSWBk8GIoCoWYYTE/d+qCWccgmRH0uXotXRDjUbc=
github.com/filecoin-project/go-statemachine v1.0.2/go.mod h1:jZdXXiHa61n4NmgWFG4w8tnqgvZVHYbJ3yW7+y8bF54= github.com/filecoin-project/go-statemachine v1.0.2/go.mod h1:jZdXXiHa61n4NmgWFG4w8tnqgvZVHYbJ3yW7+y8bF54=