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
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")

View File

@ -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,9 +53,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(),
@ -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")

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)
}
fmt.Println("found ", m.Ret, " and ", m.Params, " for ", m.Num)
return reflect.New(m.Ret.Elem()).Interface().(cbg.CBORUnmarshaler), nil
}

View File

@ -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,11 +90,16 @@ func (ar *ActorRegistry) Register(av actorstypes.Version, pred ActorPredicate, v
pred = func(vmr.Runtime, cid.Cid) error { return nil }
}
for _, a := range vmactors {
var code nativeCode
var err error
if av <= actorstypes.Version7 {
// register in the `actors` map (for the invoker)
code, err := ar.transform(a)
code, err = ar.transform(a)
if err != nil {
panic(xerrors.Errorf("%s: %w", string(a.Code().Hash()), err))
}
}
ai := &actorInfo{
methods: code,
@ -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),
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
}

View File

@ -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)
{

View File

@ -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 {

View File

@ -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")
})
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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

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)
}
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

View File

@ -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

2
go.mod
View File

@ -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

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.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=