diff --git a/api/api_full.go b/api/api_full.go index bb1eb159540..0b9fb71d241 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -344,6 +344,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 *MessageMatch, tsk types.TipSetKey, toht abi.ChainEpoch) ([]cid.Cid, error) + // StateDecodeParams attempts to decode the provided params, based on the recipient actor address and method number. + StateDecodeParams(ctx context.Context, toAddr address.Address, method abi.MethodNum, params []byte, tsk types.TipSetKey) (interface{}, error) // StateNetworkName returns the name of the network the node is synced to StateNetworkName(context.Context) (dtypes.NetworkName, error) diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index 3a4ae75a8f3..35f279c3d6a 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -215,6 +215,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 *api.MessageMatch, tsk types.TipSetKey, toht abi.ChainEpoch) ([]cid.Cid, error) `perm:"read"` + StateDecodeParams func(context.Context, address.Address, abi.MethodNum, []byte, types.TipSetKey) (interface{}, error) `perm:"read"` StateCompute func(context.Context, abi.ChainEpoch, []*types.Message, types.TipSetKey) (*api.ComputeStateOutput, error) `perm:"read"` StateVerifierStatus func(context.Context, address.Address, types.TipSetKey) (*abi.StoragePower, error) `perm:"read"` StateVerifiedClientStatus func(context.Context, address.Address, types.TipSetKey) (*abi.StoragePower, error) `perm:"read"` @@ -1014,6 +1015,10 @@ func (c *FullNodeStruct) StateListMessages(ctx context.Context, match *api.Messa return c.Internal.StateListMessages(ctx, match, tsk, toht) } +func (c *FullNodeStruct) StateDecodeParams(ctx context.Context, toAddr address.Address, method abi.MethodNum, params []byte, tsk types.TipSetKey) (interface{}, error) { + return c.Internal.StateDecodeParams(ctx, toAddr, method, params, 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) } diff --git a/chain/stmgr/utils.go b/chain/stmgr/utils.go index 5b144281d53..78121cc4c52 100644 --- a/chain/stmgr/utils.go +++ b/chain/stmgr/utils.go @@ -614,6 +614,14 @@ func GetReturnType(ctx context.Context, sm *StateManager, to address.Address, me return reflect.New(m.Ret.Elem()).Interface().(cbg.CBORUnmarshaler), nil } +func GetParamType(actCode cid.Cid, method abi.MethodNum) (cbg.CBORUnmarshaler, error) { + m, found := MethodsMap[actCode][method] + if !found { + return nil, fmt.Errorf("unknown method %d for actor %s", method, actCode) + } + return reflect.New(m.Params.Elem()).Interface().(cbg.CBORUnmarshaler), nil +} + func minerHasMinPower(ctx context.Context, sm *StateManager, addr address.Address, ts *types.TipSet) (bool, error) { pact, err := sm.LoadActor(ctx, power.Address, ts) if err != nil { diff --git a/cli/state.go b/cli/state.go index 13aa5c39b8c..c17a4f438ba 100644 --- a/cli/state.go +++ b/cli/state.go @@ -1299,12 +1299,11 @@ func sumGas(changes []*types.GasTrace) types.GasTrace { } func JsonParams(code cid.Cid, method abi.MethodNum, params []byte) (string, error) { - methodMeta, found := stmgr.MethodsMap[code][method] - if !found { - return "", fmt.Errorf("method %d not found on actor %s", method, code) + p, err := stmgr.GetParamType(code, method) + if err != nil { + return "", err } - re := reflect.New(methodMeta.Params.Elem()) - p := re.Interface().(cbg.CBORUnmarshaler) + if err := p.UnmarshalCBOR(bytes.NewReader(params)); err != nil { return "", err } diff --git a/documentation/en/api-methods.md b/documentation/en/api-methods.md index 4e18d85c919..4ab89d688fe 100644 --- a/documentation/en/api-methods.md +++ b/documentation/en/api-methods.md @@ -140,6 +140,7 @@ * [StateCirculatingSupply](#StateCirculatingSupply) * [StateCompute](#StateCompute) * [StateDealProviderCollateralBounds](#StateDealProviderCollateralBounds) + * [StateDecodeParams](#StateDecodeParams) * [StateGetActor](#StateGetActor) * [StateGetReceipt](#StateGetReceipt) * [StateListActors](#StateListActors) @@ -3387,6 +3388,31 @@ Response: } ``` +### StateDecodeParams +StateDecodeParams attempts to decode the provided params, based on the recipient actor address and method number. + + +Perms: read + +Inputs: +```json +[ + "f01234", + 1, + "Ynl0ZSBhcnJheQ==", + [ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + { + "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" + } + ] +] +``` + +Response: `{}` + ### StateGetActor StateGetActor returns the indicated actor's nonce and balance. diff --git a/node/impl/full/state.go b/node/impl/full/state.go index 9a6c772ae3c..126ff0d7bb7 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -491,6 +491,24 @@ func (a *StateAPI) StateReadState(ctx context.Context, actor address.Address, ts }, nil } +func (a *StateAPI) StateDecodeParams(ctx context.Context, toAddr address.Address, method abi.MethodNum, params []byte, tsk types.TipSetKey) (interface{}, error) { + act, err := a.StateGetActor(ctx, toAddr, tsk) + if err != nil { + return nil, xerrors.Errorf("getting actor: %w", err) + } + + paramType, err := stmgr.GetParamType(act.Code, method) + if err != nil { + return nil, xerrors.Errorf("getting params type: %w", err) + } + + if err = paramType.UnmarshalCBOR(bytes.NewReader(params)); err != nil { + return nil, err + } + + return paramType, nil +} + // 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)