2019-07-26 04:54:22 +00:00
package vm
2019-07-11 15:38:37 +00:00
import (
2019-09-10 19:58:45 +00:00
"bytes"
2019-11-02 14:13:21 +00:00
"encoding/hex"
2019-07-11 15:38:37 +00:00
"fmt"
"reflect"
2021-09-02 16:07:23 +00:00
"runtime"
"strings"
2019-07-11 15:38:37 +00:00
2020-11-09 01:48:05 +00:00
"github.com/filecoin-project/go-state-types/network"
2020-09-29 04:24:38 +00:00
"github.com/filecoin-project/lotus/chain/actors/builtin"
2019-08-16 12:11:57 +00:00
"github.com/ipfs/go-cid"
2019-09-10 19:58:45 +00:00
cbg "github.com/whyrusleeping/cbor-gen"
2019-08-16 12:11:57 +00:00
"golang.org/x/xerrors"
2021-11-04 15:59:29 +00:00
vmr "github.com/filecoin-project/specs-actors/v7/actors/runtime"
2020-09-18 21:21:05 +00:00
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/exitcode"
2020-09-28 19:48:08 +00:00
rtt "github.com/filecoin-project/go-state-types/rt"
2020-09-18 21:21:05 +00:00
2020-09-25 00:51:34 +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"
2020-09-25 00:51:34 +00:00
"github.com/filecoin-project/lotus/chain/types"
2019-07-11 15:38:37 +00:00
)
2021-09-02 16:07:23 +00:00
type MethodMeta struct {
Name string
Params reflect . Type
Ret reflect . Type
}
2020-09-25 00:51:34 +00:00
type ActorRegistry struct {
actors map [ cid . Cid ] * actorInfo
2021-09-02 16:07:23 +00:00
Methods map [ cid . Cid ] map [ abi . MethodNum ] MethodMeta
2019-07-11 15:38:37 +00:00
}
2020-09-28 19:48:08 +00:00
// 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 )
}
2020-11-18 09:22:58 +00:00
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 ( ) )
2020-09-28 19:48:08 +00:00
}
return nil
}
}
2020-09-18 21:21:05 +00:00
type invokeFunc func ( rt vmr . Runtime , params [ ] byte ) ( [ ] byte , aerrors . ActorError )
2019-07-11 15:38:37 +00:00
type nativeCode [ ] invokeFunc
2020-09-25 00:51:34 +00:00
type actorInfo struct {
2020-09-28 19:48:08 +00:00
methods nativeCode
vmActor rtt . VMActor
2020-09-25 00:51:34 +00:00
// TODO: consider making this a network version range?
2020-09-28 19:48:08 +00:00
predicate ActorPredicate
2020-09-25 00:51:34 +00:00
}
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 { } ,
}
2019-07-11 15:38:37 +00:00
}
2020-09-25 00:51:34 +00:00
func ( ar * ActorRegistry ) Invoke ( codeCid cid . Cid , rt vmr . Runtime , method abi . MethodNum , params [ ] byte ) ( [ ] byte , aerrors . ActorError ) {
act , ok := ar . actors [ codeCid ]
2019-07-11 15:38:37 +00:00
if ! ok {
2020-09-14 11:45:20 +00:00
log . Errorf ( "no code for actor %s (Addr: %s)" , codeCid , rt . Receiver ( ) )
2020-04-03 00:09:41 +00:00
return nil , aerrors . Newf ( exitcode . SysErrorIllegalActor , "no code for actor %s(%d)(%s)" , codeCid , method , hex . EncodeToString ( params ) )
2019-07-11 15:38:37 +00:00
}
2020-10-07 22:46:56 +00:00
if err := act . predicate ( rt , act . vmActor ) ; err != nil {
return nil , aerrors . Newf ( exitcode . SysErrorIllegalActor , "unsupported actor: %s" , err )
}
2020-09-25 00:51:34 +00:00
if method >= abi . MethodNum ( len ( act . methods ) ) || act . methods [ method ] == nil {
2020-04-02 18:24:38 +00:00
return nil , aerrors . Newf ( exitcode . SysErrInvalidMethod , "no method %d on actor" , method )
2019-07-11 15:38:37 +00:00
}
2020-09-25 00:51:34 +00:00
return act . methods [ method ] ( rt , params )
2019-07-11 15:38:37 +00:00
}
2022-04-08 00:03:07 +00:00
func ( ar * ActorRegistry ) Register ( av actors . Version , pred ActorPredicate , vmactors ... rtt . VMActor ) {
2020-09-28 19:48:08 +00:00
if pred == nil {
pred = func ( vmr . Runtime , rtt . VMActor ) error { return nil }
2019-07-11 15:38:37 +00:00
}
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)
2020-09-28 19:48:08 +00:00
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 {
2020-09-28 19:48:08 +00:00
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
2022-04-08 10:48:17 +00:00
// 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 ) )
2022-04-08 10:48:17 +00:00
var ok bool
realCode , ok = actors . GetActorCodeID ( av , name )
2022-04-08 00:03:07 +00:00
if ok {
2022-04-08 10:48:17 +00:00
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 ) ,
}
}
ar . Methods [ a . Code ( ) ] = methods
2022-04-08 10:48:17 +00:00
if realCode . Defined ( ) {
ar . Methods [ realCode ] = methods
}
2020-09-25 00:51:34 +00:00
}
}
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." )
}
2020-09-28 19:48:08 +00:00
if err := act . predicate ( rt , act . vmActor ) ; err != nil {
return nil , aerrors . Newf ( exitcode . SysErrorIllegalArgument , "Cannot create actor: %w" , err )
2020-09-25 00:51:34 +00:00
}
2020-09-28 19:48:08 +00:00
if rtt . IsSingletonActor ( act . vmActor ) {
2020-09-25 00:51:34 +00:00
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
2019-07-11 15:38:37 +00:00
}
2020-09-28 19:48:08 +00:00
type invokee interface {
2019-07-11 16:15:44 +00:00
Exports ( ) [ ] interface { }
}
2020-09-28 19:48:08 +00:00
func ( * ActorRegistry ) transform ( instance invokee ) ( nativeCode , error ) {
2020-01-28 23:17:25 +00:00
itype := reflect . TypeOf ( instance )
exports := instance . Exports ( )
2020-09-23 23:15:37 +00:00
runtimeType := reflect . TypeOf ( ( * vmr . Runtime ) ( nil ) ) . Elem ( )
2020-01-28 23:17:25 +00:00
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" )
2020-01-28 23:17:25 +00:00
}
if t . In ( 1 ) . Kind ( ) != reflect . Ptr {
2020-09-07 11:29:40 +00:00
return nil , newErr ( "second argument should be of kind reflect.Ptr" )
2020-01-28 23:17:25 +00:00
}
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 {
2020-09-04 03:27:59 +00:00
if m == nil {
continue
}
2020-01-28 23:17:25 +00:00
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 )
2020-11-09 01:48:05 +00:00
rt := in [ 0 ] . Interface ( ) . ( * Runtime )
2020-04-03 00:09:41 +00:00
inBytes := in [ 1 ] . Interface ( ) . ( [ ] byte )
2020-04-03 21:38:11 +00:00
if err := DecodeParams ( inBytes , param . Interface ( ) ) ; err != nil {
2020-11-09 01:48:05 +00:00
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 ( ) ,
2020-01-28 23:17:25 +00:00
}
}
2020-03-10 02:24:02 +00:00
rval , aerror := rt . shimCall ( func ( ) interface { } {
2020-01-28 23:17:25 +00:00
ret := meth . Call ( [ ] reflect . Value {
2020-03-10 02:24:02 +00:00
reflect . ValueOf ( rt ) ,
2020-01-28 23:17:25 +00:00
param ,
} )
return ret [ 0 ] . Interface ( )
} )
return [ ] reflect . Value {
reflect . ValueOf ( & rval ) . Elem ( ) ,
reflect . ValueOf ( & aerror ) . Elem ( ) ,
}
} ) . Interface ( ) . ( invokeFunc )
}
return code , nil
}
2019-09-10 19:58:45 +00:00
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
}
2020-09-25 00:51:34 +00:00
actInfo , ok := i . actors [ act . Code ]
2019-08-16 02:33:59 +00:00
if ! ok {
2020-09-25 00:51:34 +00:00
return nil , xerrors . Errorf ( "state type for actor %s not found" , act . Code )
2019-08-16 02:33:59 +00:00
}
2020-09-28 19:48:08 +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
}
2020-09-28 19:48:08 +00:00
return um , nil
2019-08-16 02:33:59 +00:00
}