lotus/chain/vm/invoker.go

203 lines
6.5 KiB
Go
Raw Normal View History

2019-07-26 04:54:22 +00:00
package vm
import (
"bytes"
"encoding/hex"
"fmt"
"reflect"
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
v0builtin "github.com/filecoin-project/specs-actors/actors/builtin"
v0account "github.com/filecoin-project/specs-actors/actors/builtin/account"
v0cron "github.com/filecoin-project/specs-actors/actors/builtin/cron"
v0init "github.com/filecoin-project/specs-actors/actors/builtin/init"
v0market "github.com/filecoin-project/specs-actors/actors/builtin/market"
2020-09-17 02:34:13 +00:00
v0miner "github.com/filecoin-project/specs-actors/actors/builtin/miner"
2020-09-17 06:50:59 +00:00
v0msig "github.com/filecoin-project/specs-actors/actors/builtin/multisig"
2020-09-18 21:21:05 +00:00
v0paych "github.com/filecoin-project/specs-actors/actors/builtin/paych"
v0power "github.com/filecoin-project/specs-actors/actors/builtin/power"
2020-09-17 06:42:39 +00:00
v0reward "github.com/filecoin-project/specs-actors/actors/builtin/reward"
2020-09-18 21:21:05 +00:00
v0system "github.com/filecoin-project/specs-actors/actors/builtin/system"
v0verifreg "github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
2020-02-08 02:18:32 +00:00
vmr "github.com/filecoin-project/specs-actors/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"
"github.com/filecoin-project/lotus/chain/actors/aerrors"
)
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
}
2020-09-18 21:21:05 +00:00
type invokeFunc func(rt vmr.Runtime, params []byte) ([]byte, aerrors.ActorError)
type nativeCode []invokeFunc
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),
}
// add builtInCode using: register(cid, singleton)
// NETUPGRADE: register code IDs for v2, etc.
2020-09-18 21:21:05 +00:00
inv.Register(v0builtin.SystemActorCodeID, v0system.Actor{}, abi.EmptyValue{})
inv.Register(v0builtin.InitActorCodeID, v0init.Actor{}, v0init.State{})
inv.Register(v0builtin.RewardActorCodeID, v0reward.Actor{}, v0reward.State{})
inv.Register(v0builtin.CronActorCodeID, v0cron.Actor{}, v0cron.State{})
inv.Register(v0builtin.StoragePowerActorCodeID, v0power.Actor{}, v0power.State{})
inv.Register(v0builtin.StorageMarketActorCodeID, v0market.Actor{}, v0market.State{})
inv.Register(v0builtin.StorageMinerActorCodeID, v0miner.Actor{}, v0miner.State{})
inv.Register(v0builtin.MultisigActorCodeID, v0msig.Actor{}, v0msig.State{})
inv.Register(v0builtin.PaymentChannelActorCodeID, v0paych.Actor{}, v0paych.State{})
inv.Register(v0builtin.VerifiedRegistryActorCodeID, v0verifreg.Actor{}, v0verifreg.State{})
inv.Register(v0builtin.AccountActorCodeID, v0account.Actor{}, v0account.State{})
return inv
}
2020-09-18 21:21:05 +00:00
func (inv *Invoker) Invoke(codeCid cid.Cid, rt vmr.Runtime, method abi.MethodNum, params []byte) ([]byte, aerrors.ActorError) {
code, ok := inv.builtInCode[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 method >= abi.MethodNum(len(code)) || code[method] == nil {
return nil, aerrors.Newf(exitcode.SysErrInvalidMethod, "no method %d on actor", method)
}
return code[method](rt, params)
}
func (inv *Invoker) Register(c cid.Cid, instance Invokee, state interface{}) {
code, err := inv.transform(instance)
if err != nil {
panic(xerrors.Errorf("%s: %w", string(c.Hash()), err))
}
inv.builtInCode[c] = code
2019-08-16 02:33:59 +00:00
inv.builtInState[c] = reflect.TypeOf(state)
}
type Invokee interface {
Exports() []interface{}
}
func (*Invoker) transform(instance Invokee) (nativeCode, error) {
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 {
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)
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(),
}
}
rt := in[0].Interface().(*Runtime)
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))
}
2019-08-16 02:33:59 +00:00
func DumpActorState(code cid.Cid, b []byte) (interface{}, error) {
2020-09-18 21:21:05 +00:00
if code == v0builtin.AccountActorCodeID { // Account code special case
2020-01-19 16:18:47 +00:00
return nil, nil
}
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
}