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-09-07 03:49:10 +00:00
|
|
|
"github.com/filecoin-project/go-state-types/exitcode"
|
2020-03-23 20:44:06 +00:00
|
|
|
"github.com/filecoin-project/specs-actors/actors/builtin/account"
|
2020-04-24 22:25:33 +00:00
|
|
|
"github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
|
2020-03-23 20:44:06 +00:00
|
|
|
|
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-07 03:49:10 +00:00
|
|
|
"github.com/filecoin-project/go-state-types/abi"
|
2020-02-14 21:38:18 +00:00
|
|
|
"github.com/filecoin-project/specs-actors/actors/builtin"
|
2020-02-25 20:35:15 +00:00
|
|
|
"github.com/filecoin-project/specs-actors/actors/builtin/cron"
|
|
|
|
init_ "github.com/filecoin-project/specs-actors/actors/builtin/init"
|
2020-02-14 21:38:18 +00:00
|
|
|
"github.com/filecoin-project/specs-actors/actors/builtin/market"
|
|
|
|
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
2020-02-08 02:18:32 +00:00
|
|
|
"github.com/filecoin-project/specs-actors/actors/builtin/multisig"
|
2020-02-25 20:35:15 +00:00
|
|
|
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
|
|
|
|
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
2020-03-04 21:21:24 +00:00
|
|
|
"github.com/filecoin-project/specs-actors/actors/builtin/reward"
|
2020-02-14 21:38:18 +00:00
|
|
|
"github.com/filecoin-project/specs-actors/actors/builtin/system"
|
2020-03-10 02:24:02 +00:00
|
|
|
"github.com/filecoin-project/specs-actors/actors/runtime"
|
2020-02-08 02:18:32 +00:00
|
|
|
vmr "github.com/filecoin-project/specs-actors/actors/runtime"
|
2020-02-14 21:38:18 +00:00
|
|
|
"github.com/filecoin-project/specs-actors/actors/util/adt"
|
2020-02-08 02:18:32 +00:00
|
|
|
|
2019-10-18 04:47:41 +00:00
|
|
|
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
2019-07-11 15:38:37 +00:00
|
|
|
)
|
|
|
|
|
2020-06-02 14:29:39 +00:00
|
|
|
type Invoker struct {
|
2019-08-16 02:34:23 +00:00
|
|
|
builtInCode map[cid.Cid]nativeCode
|
2019-08-16 02:33:59 +00:00
|
|
|
builtInState map[cid.Cid]reflect.Type
|
2019-07-11 15:38:37 +00:00
|
|
|
}
|
|
|
|
|
2020-04-03 00:09:41 +00:00
|
|
|
type invokeFunc func(rt runtime.Runtime, params []byte) ([]byte, aerrors.ActorError)
|
2019-07-11 15:38:37 +00:00
|
|
|
type nativeCode []invokeFunc
|
|
|
|
|
2020-06-02 14:29:39 +00:00
|
|
|
func NewInvoker() *Invoker {
|
|
|
|
inv := &Invoker{
|
2019-08-16 02:34:23 +00:00
|
|
|
builtInCode: make(map[cid.Cid]nativeCode),
|
2019-08-16 02:33:59 +00:00
|
|
|
builtInState: make(map[cid.Cid]reflect.Type),
|
2019-07-11 15:38:37 +00:00
|
|
|
}
|
2019-07-12 03:59:55 +00:00
|
|
|
|
2019-07-11 15:38:37 +00:00
|
|
|
// add builtInCode using: register(cid, singleton)
|
2020-02-14 21:38:18 +00:00
|
|
|
inv.Register(builtin.SystemActorCodeID, system.Actor{}, adt.EmptyValue{})
|
2020-02-25 20:54:58 +00:00
|
|
|
inv.Register(builtin.InitActorCodeID, init_.Actor{}, init_.State{})
|
2020-03-04 21:21:24 +00:00
|
|
|
inv.Register(builtin.RewardActorCodeID, reward.Actor{}, reward.State{})
|
2020-02-25 20:54:58 +00:00
|
|
|
inv.Register(builtin.CronActorCodeID, cron.Actor{}, cron.State{})
|
|
|
|
inv.Register(builtin.StoragePowerActorCodeID, power.Actor{}, power.State{})
|
|
|
|
inv.Register(builtin.StorageMarketActorCodeID, market.Actor{}, market.State{})
|
|
|
|
inv.Register(builtin.StorageMinerActorCodeID, miner.Actor{}, miner.State{})
|
|
|
|
inv.Register(builtin.MultisigActorCodeID, multisig.Actor{}, multisig.State{})
|
|
|
|
inv.Register(builtin.PaymentChannelActorCodeID, paych.Actor{}, paych.State{})
|
2020-04-24 22:25:33 +00:00
|
|
|
inv.Register(builtin.VerifiedRegistryActorCodeID, verifreg.Actor{}, verifreg.State{})
|
2020-03-23 09:43:08 +00:00
|
|
|
inv.Register(builtin.AccountActorCodeID, account.Actor{}, account.State{})
|
2019-07-12 03:59:55 +00:00
|
|
|
|
2019-07-11 15:38:37 +00:00
|
|
|
return inv
|
|
|
|
}
|
|
|
|
|
2020-06-02 14:29:39 +00:00
|
|
|
func (inv *Invoker) Invoke(codeCid cid.Cid, rt runtime.Runtime, method abi.MethodNum, params []byte) ([]byte, aerrors.ActorError) {
|
2019-07-11 15:38:37 +00:00
|
|
|
|
2020-04-03 00:09:41 +00:00
|
|
|
code, ok := inv.builtInCode[codeCid]
|
2019-07-11 15:38:37 +00:00
|
|
|
if !ok {
|
2020-04-03 00:09:41 +00:00
|
|
|
log.Errorf("no code for actor %s (Addr: %s)", codeCid, rt.Message().Receiver())
|
|
|
|
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-02-11 01:43:26 +00:00
|
|
|
if method >= abi.MethodNum(len(code)) || code[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-04-03 00:09:41 +00:00
|
|
|
return code[method](rt, params)
|
2019-07-11 15:38:37 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-06-02 14:29:39 +00:00
|
|
|
func (inv *Invoker) Register(c cid.Cid, instance Invokee, state interface{}) {
|
2019-07-11 15:38:37 +00:00
|
|
|
code, err := inv.transform(instance)
|
|
|
|
if err != nil {
|
2020-04-10 22:20:48 +00:00
|
|
|
panic(xerrors.Errorf("%s: %w", string(c.Hash()), err))
|
2019-07-11 15:38:37 +00:00
|
|
|
}
|
|
|
|
inv.builtInCode[c] = code
|
2019-08-16 02:33:59 +00:00
|
|
|
inv.builtInState[c] = reflect.TypeOf(state)
|
2019-07-11 15:38:37 +00:00
|
|
|
}
|
|
|
|
|
2019-07-11 16:15:44 +00:00
|
|
|
type Invokee interface {
|
|
|
|
Exports() []interface{}
|
|
|
|
}
|
|
|
|
|
2020-06-02 14:29:39 +00:00
|
|
|
func (*Invoker) transform(instance Invokee) (nativeCode, error) {
|
2020-01-28 23:17:25 +00:00
|
|
|
itype := reflect.TypeOf(instance)
|
|
|
|
exports := instance.Exports()
|
|
|
|
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 t.In(0) != reflect.TypeOf((*vmr.Runtime)(nil)).Elem() {
|
|
|
|
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-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 {
|
|
|
|
aerr := aerrors.Absorb(err, 1, "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(),
|
2020-01-28 23:17:25 +00:00
|
|
|
}
|
|
|
|
}
|
2020-04-03 00:09:41 +00:00
|
|
|
rt := in[0].Interface().(*Runtime)
|
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))
|
|
|
|
}
|
|
|
|
|
2019-08-16 02:33:59 +00:00
|
|
|
func DumpActorState(code cid.Cid, b []byte) (interface{}, error) {
|
2020-02-25 20:54:58 +00:00
|
|
|
if code == builtin.AccountActorCodeID { // Account code special case
|
2020-01-19 16:18:47 +00:00
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2020-01-31 23:51:15 +00:00
|
|
|
i := NewInvoker() // TODO: register builtins in init block
|
2019-08-16 02:33:59 +00:00
|
|
|
|
|
|
|
typ, ok := i.builtInState[code]
|
|
|
|
if !ok {
|
2019-11-28 18:08:10 +00:00
|
|
|
return nil, xerrors.Errorf("state type for actor %s not found", code)
|
2019-08-16 02:33:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
rv := reflect.New(typ)
|
2019-09-13 18:16:39 +00:00
|
|
|
um, ok := rv.Interface().(cbg.CBORUnmarshaler)
|
|
|
|
if !ok {
|
|
|
|
return nil, xerrors.New("state type does not implement CBORUnmarshaler")
|
|
|
|
}
|
|
|
|
|
|
|
|
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 rv.Elem().Interface(), nil
|
|
|
|
}
|