Skip to content

Commit

Permalink
Add a StateDecodeParams method
Browse files Browse the repository at this point in the history
  • Loading branch information
arajasek committed Sep 30, 2020
1 parent 6025bce commit c308c98
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 16 deletions.
2 changes: 2 additions & 0 deletions api/api_full.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,8 @@ type FullNode interface {
StateReadState(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*ActorState, error)
// StateListMessages looks back and returns all messages with a matching to or from address, stopping at the given height.
StateListMessages(ctx context.Context, match *types.Message, tsk types.TipSetKey, toht abi.ChainEpoch) ([]cid.Cid, error)
// StateDecodeParams attempts to decode the params field of a specified msg based on the methodNum and recipient actor.
StateDecodeParams(ctx context.Context, msg cid.Cid, tsk types.TipSetKey) (interface{}, error)

// StateNetworkName returns the name of the network the node is synced to
StateNetworkName(context.Context) (dtypes.NetworkName, error)
Expand Down
5 changes: 5 additions & 0 deletions api/apistruct/struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ type FullNodeStruct struct {
StateGetReceipt func(context.Context, cid.Cid, types.TipSetKey) (*types.MessageReceipt, error) `perm:"read"`
StateMinerSectorCount func(context.Context, address.Address, types.TipSetKey) (api.MinerSectors, error) `perm:"read"`
StateListMessages func(ctx context.Context, match *types.Message, tsk types.TipSetKey, toht abi.ChainEpoch) ([]cid.Cid, error) `perm:"read"`
StateDecodeParams func(ctx context.Context, msg cid.Cid, tsk types.TipSetKey) (interface{}, error) `perm:"read"`
StateCompute func(context.Context, abi.ChainEpoch, []*types.Message, types.TipSetKey) (*api.ComputeStateOutput, error) `perm:"read"`
StateVerifiedClientStatus func(context.Context, address.Address, types.TipSetKey) (*abi.StoragePower, error) `perm:"read"`
StateDealProviderCollateralBounds func(context.Context, abi.PaddedPieceSize, bool, types.TipSetKey) (api.DealCollateralBounds, error) `perm:"read"`
Expand Down Expand Up @@ -879,6 +880,10 @@ func (c *FullNodeStruct) StateListMessages(ctx context.Context, match *types.Mes
return c.Internal.StateListMessages(ctx, match, tsk, toht)
}

func (c *FullNodeStruct) StateDecodeParams(ctx context.Context, msg cid.Cid, tsk types.TipSetKey) (interface{}, error) {
return c.Internal.StateDecodeParams(ctx, msg, tsk)
}

func (c *FullNodeStruct) StateCompute(ctx context.Context, height abi.ChainEpoch, msgs []*types.Message, tsk types.TipSetKey) (*api.ComputeStateOutput, error) {
return c.Internal.StateCompute(ctx, height, msgs, tsk)
}
Expand Down
64 changes: 48 additions & 16 deletions chain/vm/invoker.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,20 @@ import (
)

type Invoker struct {
builtInCode map[cid.Cid]nativeCode
builtInState map[cid.Cid]reflect.Type
builtInCode map[cid.Cid]nativeCode
builtInState map[cid.Cid]reflect.Type
builtInParams map[cid.Cid]paramsList
}

type invokeFunc func(rt vmr.Runtime, params []byte) ([]byte, aerrors.ActorError)
type nativeCode []invokeFunc
type paramsList []reflect.Type

func NewInvoker() *Invoker {
inv := &Invoker{
builtInCode: make(map[cid.Cid]nativeCode),
builtInState: make(map[cid.Cid]reflect.Type),
builtInCode: make(map[cid.Cid]nativeCode),
builtInState: make(map[cid.Cid]reflect.Type),
builtInParams: make(map[cid.Cid]paramsList),
}

// add builtInCode using: register(cid, singleton)
Expand Down Expand Up @@ -76,19 +79,20 @@ func (inv *Invoker) Invoke(codeCid cid.Cid, rt vmr.Runtime, method abi.MethodNum
}

func (inv *Invoker) Register(c cid.Cid, instance Invokee, state interface{}) {
code, err := inv.transform(instance)
code, params, err := inv.transform(instance)
if err != nil {
panic(xerrors.Errorf("%s: %w", string(c.Hash()), err))
}
inv.builtInCode[c] = code
inv.builtInState[c] = reflect.TypeOf(state)
inv.builtInParams[c] = params
}

type Invokee interface {
Exports() []interface{}
}

func (*Invoker) transform(instance Invokee) (nativeCode, error) {
func (*Invoker) transform(instance Invokee) (nativeCode, paramsList, error) {
itype := reflect.TypeOf(instance)
exports := instance.Exports()
for i, m := range exports {
Expand All @@ -105,39 +109,40 @@ func (*Invoker) transform(instance Invokee) (nativeCode, error) {
meth := reflect.ValueOf(m)
t := meth.Type()
if t.Kind() != reflect.Func {
return nil, newErr("is not a function")
return nil, nil, newErr("is not a function")
}
if t.NumIn() != 2 {
return nil, newErr("wrong number of inputs should be: " +
return nil, 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")
return nil, 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")
return nil, nil, newErr("second argument should be of kind reflect.Ptr")
}

if t.NumOut() != 1 {
return nil, newErr("wrong number of outputs should be: " +
return nil, 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")
return nil, nil, newErr("output needs to implement cgb.CBORMarshaler")
}
}
code := make(nativeCode, len(exports))
params := make(paramsList, len(exports))
for id, m := range exports {
if m == nil {
continue
}
meth := reflect.ValueOf(m)
paramT := meth.Type().In(1).Elem()
param := reflect.New(paramT)
params[id] = paramT
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)
if err := DecodeParams(inBytes, param.Interface()); err != nil {
aerr := aerrors.Absorb(err, 1, "failed to decode parameters")
Expand All @@ -164,7 +169,7 @@ func (*Invoker) transform(instance Invokee) (nativeCode, error) {
}).Interface().(invokeFunc)

}
return code, nil
return code, params, nil
}

func DecodeParams(b []byte, out interface{}) error {
Expand Down Expand Up @@ -200,3 +205,30 @@ func DumpActorState(code cid.Cid, b []byte) (interface{}, error) {

return rv.Elem().Interface(), nil
}

// TODO: ActorUpgrade: Support versioning
// TODO: Doesn't work well for methods that have params as params (Init's Exec, multisig Propose, etc.)
func DumpParams(code cid.Cid, method abi.MethodNum, b []byte) (interface{}, error) {
if code == builtin0.AccountActorCodeID { // Account code special case
return nil, nil
}

i := NewInvoker() // TODO: register builtins in init block

paramTypes, ok := i.builtInParams[code]
if !ok {
return nil, xerrors.Errorf("params list for actor %s not found", code)
}

params := reflect.New(paramTypes[method])
um, ok := params.Interface().(cbg.CBORUnmarshaler)
if !ok {
return nil, xerrors.New("param does not implement CBORUnmarshaler")
}

if err := um.UnmarshalCBOR(bytes.NewReader(b)); err != nil {
return nil, xerrors.Errorf("unmarshaling param: %w", err)
}

return params.Interface(), nil
}
23 changes: 23 additions & 0 deletions node/impl/full/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,29 @@ func (a *StateAPI) StateReadState(ctx context.Context, actor address.Address, ts
}, nil
}

func (a *StateAPI) StateDecodeParams(ctx context.Context, msgCid cid.Cid, tsk types.TipSetKey) (interface{}, error) {
ts, err := a.Chain.GetTipSetFromKey(tsk)
if err != nil {
return nil, xerrors.Errorf("loading tipset %s: %w", tsk, err)
}
state, err := a.stateForTs(ctx, ts)
if err != nil {
return nil, xerrors.Errorf("getting state for tipset: %w", err)
}

msg, err := a.Chain.GetMessage(msgCid)
if err != nil {
return nil, xerrors.Errorf("loading message: %w", err)
}

act, err := state.GetActor(msg.To)
if err != nil {
return nil, xerrors.Errorf("getting actor: %w", err)
}

return vm.DumpParams(act.Code, msg.Method, msg.Params)
}

// This is on StateAPI because miner.Miner requires this, and MinerAPI requires miner.Miner
func (a *StateAPI) MinerGetBaseInfo(ctx context.Context, maddr address.Address, epoch abi.ChainEpoch, tsk types.TipSetKey) (*api.MiningBaseInfo, error) {
return stmgr.MinerGetBaseInfo(ctx, a.StateManager, a.Beacon, tsk, epoch, maddr, a.ProofVerifier)
Expand Down

0 comments on commit c308c98

Please sign in to comment.