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"
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"
2020-09-28 19:48:08 +00:00
exported0 "github.com/filecoin-project/specs-actors/actors/builtin/exported"
2020-09-28 20:13:18 +00:00
exported2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/exported"
2021-01-19 07:34:55 +00:00
exported3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/exported"
2021-04-23 12:29:46 +00:00
exported4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/exported"
2021-05-06 05:44:11 +00:00
exported5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/exported"
2021-03-10 15:16:44 +00:00
vmr "github.com/filecoin-project/specs-actors/v5/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
)
2020-09-25 00:51:34 +00:00
type ActorRegistry struct {
actors map [ cid . Cid ] * actorInfo
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 {
2020-11-18 09:22:58 +00:00
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 ( ) )
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 {
inv := & ActorRegistry { actors : make ( map [ cid . Cid ] * actorInfo ) }
// TODO: define all these properties on the actors themselves, in specs-actors.
2019-07-12 03:59:55 +00:00
2019-07-11 15:38:37 +00:00
// add builtInCode using: register(cid, singleton)
2020-09-28 19:48:08 +00:00
inv . Register ( ActorsVersionPredicate ( actors . Version0 ) , exported0 . BuiltinActors ( ) ... )
2020-09-28 20:13:18 +00:00
inv . Register ( ActorsVersionPredicate ( actors . Version2 ) , exported2 . BuiltinActors ( ) ... )
2021-01-19 07:34:55 +00:00
inv . Register ( ActorsVersionPredicate ( actors . Version3 ) , exported3 . BuiltinActors ( ) ... )
2021-04-23 12:29:46 +00:00
inv . Register ( ActorsVersionPredicate ( actors . Version4 ) , exported4 . BuiltinActors ( ) ... )
2021-05-06 05:44:11 +00:00
inv . Register ( ActorsVersionPredicate ( actors . Version5 ) , exported5 . BuiltinActors ( ) ... )
2020-09-19 03:46:03 +00:00
2019-07-11 15:38:37 +00:00
return inv
}
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
}
2020-09-28 19:48:08 +00:00
func ( ar * ActorRegistry ) Register ( pred ActorPredicate , actors ... rtt . VMActor ) {
if pred == nil {
pred = func ( vmr . Runtime , rtt . VMActor ) error { return nil }
2019-07-11 15:38:37 +00:00
}
2020-09-28 19:48:08 +00:00
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 ,
}
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 ) ) {
2020-01-28 23:17:25 +00:00
return nil , newErr ( "first arguemnt should be vmr.Runtime" )
}
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 ) )
}
2020-09-25 00:51:34 +00:00
func DumpActorState ( 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
i := NewActorRegistry ( ) // TODO: register builtins in init block
2019-08-16 02:33:59 +00:00
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
}