lotus/chain/vm/invoker.go

307 lines
8.5 KiB
Go
Raw Normal View History

2019-07-26 04:54:22 +00:00
package vm
import (
"bytes"
"encoding/hex"
"fmt"
"reflect"
2021-09-02 16:07:23 +00:00
"runtime"
"strings"
2019-08-16 12:11:57 +00:00
"github.com/ipfs/go-cid"
cbg "github.com/whyrusleeping/cbor-gen"
2019-08-16 12:11:57 +00:00
"golang.org/x/xerrors"
2020-09-18 21:21:05 +00:00
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/exitcode"
2022-06-14 15:00:51 +00:00
"github.com/filecoin-project/go-state-types/network"
rtt "github.com/filecoin-project/go-state-types/rt"
2022-06-14 15:00:51 +00:00
vmr "github.com/filecoin-project/specs-actors/v7/actors/runtime"
2020-09-18 21:21:05 +00:00
"github.com/filecoin-project/lotus/chain/actors"
2020-09-18 21:21:05 +00:00
"github.com/filecoin-project/lotus/chain/actors/aerrors"
2022-06-14 15:00:51 +00:00
"github.com/filecoin-project/lotus/chain/actors/builtin"
"github.com/filecoin-project/lotus/chain/types"
)
2021-09-02 16:07:23 +00:00
type MethodMeta struct {
Name string
Params reflect.Type
Ret reflect.Type
}
type ActorRegistry struct {
actors map[cid.Cid]*actorInfo
2021-09-02 16:07:23 +00:00
Methods map[cid.Cid]map[abi.MethodNum]MethodMeta
}
// An ActorPredicate returns an error if the given actor is not valid for the given runtime environment (e.g., chain height, version, etc.).
type ActorPredicate func(vmr.Runtime, rtt.VMActor) error
func ActorsVersionPredicate(ver actors.Version) ActorPredicate {
return func(rt vmr.Runtime, v rtt.VMActor) error {
2021-08-10 17:07:30 +00:00
aver, err := actors.VersionForNetwork(rt.NetworkVersion())
if err != nil {
return xerrors.Errorf("unsupported network version: %w", err)
}
if aver != ver {
return xerrors.Errorf("actor %s is a version %d actor; chain only supports actor version %d at height %d and nver %d", v.Code(), ver, aver, rt.CurrEpoch(), rt.NetworkVersion())
}
return nil
}
}
2020-09-18 21:21:05 +00:00
type invokeFunc func(rt vmr.Runtime, params []byte) ([]byte, aerrors.ActorError)
type nativeCode []invokeFunc
type actorInfo struct {
methods nativeCode
vmActor rtt.VMActor
// TODO: consider making this a network version range?
predicate ActorPredicate
}
func NewActorRegistry() *ActorRegistry {
2021-09-02 16:07:23 +00:00
return &ActorRegistry{
actors: make(map[cid.Cid]*actorInfo),
Methods: map[cid.Cid]map[abi.MethodNum]MethodMeta{},
}
}
func (ar *ActorRegistry) Invoke(codeCid cid.Cid, rt vmr.Runtime, method abi.MethodNum, params []byte) ([]byte, aerrors.ActorError) {
act, ok := ar.actors[codeCid]
if !ok {
2020-09-14 11:45:20 +00:00
log.Errorf("no code for actor %s (Addr: %s)", codeCid, rt.Receiver())
return nil, aerrors.Newf(exitcode.SysErrorIllegalActor, "no code for actor %s(%d)(%s)", codeCid, method, hex.EncodeToString(params))
}
if err := act.predicate(rt, act.vmActor); err != nil {
return nil, aerrors.Newf(exitcode.SysErrorIllegalActor, "unsupported actor: %s", err)
}
if method >= abi.MethodNum(len(act.methods)) || act.methods[method] == nil {
return nil, aerrors.Newf(exitcode.SysErrInvalidMethod, "no method %d on actor", method)
}
return act.methods[method](rt, params)
}
2022-04-08 00:03:07 +00:00
func (ar *ActorRegistry) Register(av actors.Version, pred ActorPredicate, vmactors ...rtt.VMActor) {
if pred == nil {
pred = func(vmr.Runtime, rtt.VMActor) error { return nil }
}
2022-04-08 00:03:07 +00:00
for _, a := range vmactors {
2021-09-02 16:07:23 +00:00
// 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))
}
2022-04-08 00:03:07 +00:00
ai := &actorInfo{
methods: code,
vmActor: a,
predicate: pred,
}
2021-09-02 16:07:23 +00:00
2022-04-08 00:03:07 +00:00
ac := a.Code()
ar.actors[ac] = ai
// necessary to make stuff work
var realCode cid.Cid
2022-04-08 00:03:07 +00:00
if av >= actors.Version8 {
name := actors.CanonicalName(builtin.ActorNameByCode(ac))
var ok bool
realCode, ok = actors.GetActorCodeID(av, name)
2022-04-08 00:03:07 +00:00
if ok {
ar.actors[realCode] = ai
2022-04-08 00:03:07 +00:00
}
}
2021-09-02 16:07:23 +00:00
// register in the `Methods` map (used by statemanager utils)
exports := a.Exports()
methods := make(map[abi.MethodNum]MethodMeta, len(exports))
// Explicitly add send, it's special.
methods[builtin.MethodSend] = MethodMeta{
Name: "Send",
Params: reflect.TypeOf(new(abi.EmptyValue)),
Ret: reflect.TypeOf(new(abi.EmptyValue)),
}
// Iterate over exported methods. Some of these _may_ be nil and
// must be skipped.
for number, export := range exports {
if export == nil {
continue
}
ev := reflect.ValueOf(export)
et := ev.Type()
// 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")
switch abi.MethodNum(number) {
case builtin.MethodSend:
panic("method 0 is reserved for Send")
case builtin.MethodConstructor:
if fnName != "Constructor" {
panic("method 1 is reserved for Constructor")
}
}
methods[abi.MethodNum(number)] = MethodMeta{
Name: fnName,
Params: et.In(1),
Ret: et.Out(0),
}
}
if realCode.Defined() {
ar.Methods[realCode] = methods
} else {
ar.Methods[a.Code()] = methods
}
}
}
func (ar *ActorRegistry) Create(codeCid cid.Cid, rt vmr.Runtime) (*types.Actor, aerrors.ActorError) {
act, ok := ar.actors[codeCid]
if !ok {
return nil, aerrors.Newf(exitcode.SysErrorIllegalArgument, "Can only create built-in actors.")
}
if err := act.predicate(rt, act.vmActor); err != nil {
return nil, aerrors.Newf(exitcode.SysErrorIllegalArgument, "Cannot create actor: %w", err)
}
if rtt.IsSingletonActor(act.vmActor) {
return nil, aerrors.Newf(exitcode.SysErrorIllegalArgument, "Can only have one instance of singleton actors.")
}
return &types.Actor{
Code: codeCid,
Head: EmptyObjectCid,
Nonce: 0,
Balance: abi.NewTokenAmount(0),
}, nil
}
type invokee interface {
Exports() []interface{}
}
func (*ActorRegistry) transform(instance invokee) (nativeCode, error) {
itype := reflect.TypeOf(instance)
exports := instance.Exports()
2020-09-23 23:15:37 +00:00
runtimeType := reflect.TypeOf((*vmr.Runtime)(nil)).Elem()
for i, m := range exports {
i := i
newErr := func(format string, args ...interface{}) error {
str := fmt.Sprintf(format, args...)
return fmt.Errorf("transform(%s) export(%d): %s", itype.Name(), i, str)
}
if m == nil {
continue
}
meth := reflect.ValueOf(m)
t := meth.Type()
if t.Kind() != reflect.Func {
return nil, newErr("is not a function")
}
if t.NumIn() != 2 {
return nil, newErr("wrong number of inputs should be: " +
"vmr.Runtime, <parameter>")
}
2020-09-23 23:15:37 +00:00
if !runtimeType.Implements(t.In(0)) {
2021-05-18 14:51:06 +00:00
return nil, newErr("first argument should be vmr.Runtime")
}
if t.In(1).Kind() != reflect.Ptr {
return nil, newErr("second argument should be of kind reflect.Ptr")
}
if t.NumOut() != 1 {
return nil, newErr("wrong number of outputs should be: " +
"cbg.CBORMarshaler")
}
o0 := t.Out(0)
if !o0.Implements(reflect.TypeOf((*cbg.CBORMarshaler)(nil)).Elem()) {
return nil, newErr("output needs to implement cgb.CBORMarshaler")
}
}
code := make(nativeCode, len(exports))
for id, m := range exports {
if m == nil {
continue
}
meth := reflect.ValueOf(m)
code[id] = reflect.MakeFunc(reflect.TypeOf((invokeFunc)(nil)),
func(in []reflect.Value) []reflect.Value {
paramT := meth.Type().In(1).Elem()
param := reflect.New(paramT)
rt := in[0].Interface().(*Runtime)
inBytes := in[1].Interface().([]byte)
2020-04-03 21:38:11 +00:00
if err := DecodeParams(inBytes, param.Interface()); err != nil {
ec := exitcode.ErrSerialization
if rt.NetworkVersion() < network.Version7 {
ec = 1
}
aerr := aerrors.Absorb(err, ec, "failed to decode parameters")
2020-04-03 21:38:11 +00:00
return []reflect.Value{
reflect.ValueOf([]byte{}),
// Below is a hack, fixed in Go 1.13
// https://git.io/fjXU6
reflect.ValueOf(&aerr).Elem(),
}
}
rval, aerror := rt.shimCall(func() interface{} {
ret := meth.Call([]reflect.Value{
reflect.ValueOf(rt),
param,
})
return ret[0].Interface()
})
return []reflect.Value{
reflect.ValueOf(&rval).Elem(),
reflect.ValueOf(&aerror).Elem(),
}
}).Interface().(invokeFunc)
}
return code, nil
}
func DecodeParams(b []byte, out interface{}) error {
um, ok := out.(cbg.CBORUnmarshaler)
if !ok {
return fmt.Errorf("type %T does not implement UnmarshalCBOR", out)
}
return um.UnmarshalCBOR(bytes.NewReader(b))
}
2021-09-02 16:07:23 +00:00
func DumpActorState(i *ActorRegistry, act *types.Actor, b []byte) (interface{}, error) {
2020-09-29 04:24:38 +00:00
if builtin.IsAccountActor(act.Code) { // Account code special case
2020-01-19 16:18:47 +00:00
return nil, nil
}
actInfo, ok := i.actors[act.Code]
2019-08-16 02:33:59 +00:00
if !ok {
return nil, xerrors.Errorf("state type for actor %s not found", act.Code)
2019-08-16 02:33:59 +00:00
}
um := actInfo.vmActor.State()
2019-09-13 18:16:39 +00:00
if err := um.UnmarshalCBOR(bytes.NewReader(b)); err != nil {
2020-08-07 14:07:34 +00:00
return nil, xerrors.Errorf("unmarshaling actor state: %w", err)
2019-08-16 02:33:59 +00:00
}
return um, nil
2019-08-16 02:33:59 +00:00
}