245 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			245 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package vm
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"encoding/hex"
 | |
| 	"fmt"
 | |
| 	"reflect"
 | |
| 
 | |
| 	"github.com/filecoin-project/go-state-types/network"
 | |
| 
 | |
| 	"github.com/filecoin-project/lotus/chain/actors/builtin"
 | |
| 
 | |
| 	"github.com/ipfs/go-cid"
 | |
| 	cbg "github.com/whyrusleeping/cbor-gen"
 | |
| 	"golang.org/x/xerrors"
 | |
| 
 | |
| 	exported0 "github.com/filecoin-project/specs-actors/actors/builtin/exported"
 | |
| 	exported2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/exported"
 | |
| 	exported3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/exported"
 | |
| 	exported4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/exported"
 | |
| 	exported5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/exported"
 | |
| 	vmr "github.com/filecoin-project/specs-actors/v5/actors/runtime"
 | |
| 
 | |
| 	"github.com/filecoin-project/go-state-types/abi"
 | |
| 	"github.com/filecoin-project/go-state-types/exitcode"
 | |
| 	rtt "github.com/filecoin-project/go-state-types/rt"
 | |
| 
 | |
| 	"github.com/filecoin-project/lotus/chain/actors"
 | |
| 	"github.com/filecoin-project/lotus/chain/actors/aerrors"
 | |
| 	"github.com/filecoin-project/lotus/chain/types"
 | |
| )
 | |
| 
 | |
| type ActorRegistry struct {
 | |
| 	actors map[cid.Cid]*actorInfo
 | |
| }
 | |
| 
 | |
| // 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 {
 | |
| 		aver := actors.VersionForNetwork(rt.NetworkVersion())
 | |
| 		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
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 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 {
 | |
| 	inv := &ActorRegistry{actors: make(map[cid.Cid]*actorInfo)}
 | |
| 
 | |
| 	// TODO: define all these properties on the actors themselves, in specs-actors.
 | |
| 
 | |
| 	// add builtInCode using: register(cid, singleton)
 | |
| 	inv.Register(ActorsVersionPredicate(actors.Version0), exported0.BuiltinActors()...)
 | |
| 	inv.Register(ActorsVersionPredicate(actors.Version2), exported2.BuiltinActors()...)
 | |
| 	inv.Register(ActorsVersionPredicate(actors.Version3), exported3.BuiltinActors()...)
 | |
| 	inv.Register(ActorsVersionPredicate(actors.Version4), exported4.BuiltinActors()...)
 | |
| 	inv.Register(ActorsVersionPredicate(actors.Version5), exported5.BuiltinActors()...)
 | |
| 
 | |
| 	return inv
 | |
| }
 | |
| 
 | |
| 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 {
 | |
| 		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)
 | |
| 
 | |
| }
 | |
| 
 | |
| func (ar *ActorRegistry) Register(pred ActorPredicate, actors ...rtt.VMActor) {
 | |
| 	if pred == nil {
 | |
| 		pred = func(vmr.Runtime, rtt.VMActor) error { return nil }
 | |
| 	}
 | |
| 	for _, a := range actors {
 | |
| 		code, err := ar.transform(a)
 | |
| 		if err != nil {
 | |
| 			panic(xerrors.Errorf("%s: %w", string(a.Code().Hash()), err))
 | |
| 		}
 | |
| 		ar.actors[a.Code()] = &actorInfo{
 | |
| 			methods:   code,
 | |
| 			vmActor:   a,
 | |
| 			predicate: pred,
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 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()
 | |
| 	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>")
 | |
| 		}
 | |
| 		if !runtimeType.Implements(t.In(0)) {
 | |
| 			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)
 | |
| 				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")
 | |
| 					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))
 | |
| }
 | |
| 
 | |
| func DumpActorState(act *types.Actor, b []byte) (interface{}, error) {
 | |
| 	if builtin.IsAccountActor(act.Code) { // Account code special case
 | |
| 		return nil, nil
 | |
| 	}
 | |
| 
 | |
| 	i := NewActorRegistry() // TODO: register builtins in init block
 | |
| 
 | |
| 	actInfo, ok := i.actors[act.Code]
 | |
| 	if !ok {
 | |
| 		return nil, xerrors.Errorf("state type for actor %s not found", act.Code)
 | |
| 	}
 | |
| 
 | |
| 	um := actInfo.vmActor.State()
 | |
| 	if err := um.UnmarshalCBOR(bytes.NewReader(b)); err != nil {
 | |
| 		return nil, xerrors.Errorf("unmarshaling actor state: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	return um, nil
 | |
| }
 |