diff --git a/api/api_full.go b/api/api_full.go index 398df3ad171..0649ececf1f 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -434,6 +434,8 @@ type FullNode interface { StateListMessages(ctx context.Context, match *MessageMatch, tsk types.TipSetKey, toht abi.ChainEpoch) ([]cid.Cid, error) //perm:read // 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) //perm:read + // StateEncodeParams attempts to encode the provided json params to the binary from + StateEncodeParams(ctx context.Context, toActCode cid.Cid, method abi.MethodNum, params json.RawMessage) ([]byte, error) //perm:read // StateNetworkName returns the name of the network the node is synced to StateNetworkName(context.Context) (dtypes.NetworkName, error) //perm:read diff --git a/api/mocks/mock_full.go b/api/mocks/mock_full.go index 80def1c9785..a6e0e9e91cb 100644 --- a/api/mocks/mock_full.go +++ b/api/mocks/mock_full.go @@ -6,6 +6,7 @@ package mocks import ( context "context" + json "encoding/json" reflect "reflect" address "github.com/filecoin-project/go-address" @@ -2244,6 +2245,21 @@ func (mr *MockFullNodeMockRecorder) StateDecodeParams(arg0, arg1, arg2, arg3, ar return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateDecodeParams", reflect.TypeOf((*MockFullNode)(nil).StateDecodeParams), arg0, arg1, arg2, arg3, arg4) } +// StateEncodeParams mocks base method. +func (m *MockFullNode) StateEncodeParams(arg0 context.Context, arg1 cid.Cid, arg2 abi.MethodNum, arg3 json.RawMessage) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateEncodeParams", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateEncodeParams indicates an expected call of StateEncodeParams. +func (mr *MockFullNodeMockRecorder) StateEncodeParams(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateEncodeParams", reflect.TypeOf((*MockFullNode)(nil).StateEncodeParams), arg0, arg1, arg2, arg3) +} + // StateGetActor mocks base method. func (m *MockFullNode) StateGetActor(arg0 context.Context, arg1 address.Address, arg2 types.TipSetKey) (*types.Actor, error) { m.ctrl.T.Helper() diff --git a/api/proxy_gen.go b/api/proxy_gen.go index 51478da0e1b..fd521a2b5c2 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -4,6 +4,7 @@ package api import ( "context" + "encoding/json" "time" "github.com/filecoin-project/go-address" @@ -342,6 +343,8 @@ type FullNodeStruct struct { StateDecodeParams func(p0 context.Context, p1 address.Address, p2 abi.MethodNum, p3 []byte, p4 types.TipSetKey) (interface{}, error) `perm:"read"` + StateEncodeParams func(p0 context.Context, p1 cid.Cid, p2 abi.MethodNum, p3 json.RawMessage) ([]byte, error) `perm:"read"` + StateGetActor func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*types.Actor, error) `perm:"read"` StateListActors func(p0 context.Context, p1 types.TipSetKey) ([]address.Address, error) `perm:"read"` @@ -2340,6 +2343,17 @@ func (s *FullNodeStub) StateDecodeParams(p0 context.Context, p1 address.Address, return nil, ErrNotSupported } +func (s *FullNodeStruct) StateEncodeParams(p0 context.Context, p1 cid.Cid, p2 abi.MethodNum, p3 json.RawMessage) ([]byte, error) { + if s.Internal.StateEncodeParams == nil { + return *new([]byte), ErrNotSupported + } + return s.Internal.StateEncodeParams(p0, p1, p2, p3) +} + +func (s *FullNodeStub) StateEncodeParams(p0 context.Context, p1 cid.Cid, p2 abi.MethodNum, p3 json.RawMessage) ([]byte, error) { + return *new([]byte), ErrNotSupported +} + func (s *FullNodeStruct) StateGetActor(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*types.Actor, error) { if s.Internal.StateGetActor == nil { return nil, ErrNotSupported diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 846a5a0c714..ba509bb5150 100644 Binary files a/build/openrpc/full.json.gz and b/build/openrpc/full.json.gz differ diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index 276df8c7311..69585cbd40b 100644 Binary files a/build/openrpc/miner.json.gz and b/build/openrpc/miner.json.gz differ diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index fbcaec0db1b..bedd7b1207b 100644 Binary files a/build/openrpc/worker.json.gz and b/build/openrpc/worker.json.gz differ diff --git a/cli/chain.go b/cli/chain.go index e30a685dd84..875dcb21aaa 100644 --- a/cli/chain.go +++ b/cli/chain.go @@ -10,7 +10,6 @@ import ( "os" "os/exec" "path" - "reflect" "sort" "strconv" "strings" @@ -1347,7 +1346,7 @@ var ChainEncodeCmd = &cli.Command{ var chainEncodeParamsCmd = &cli.Command{ Name: "params", Usage: "Encodes the given JSON params", - ArgsUsage: "[toAddr method params]", + ArgsUsage: "[dest method params]", Flags: []cli.Flag{ &cli.StringFlag{ Name: "tipset", @@ -1357,62 +1356,65 @@ var chainEncodeParamsCmd = &cli.Command{ Value: "base64", Usage: "specify input encoding to parse", }, + &cli.BoolFlag{ + Name: "to-code", + Usage: "interpret dest as code CID instead of as address", + }, }, Action: func(cctx *cli.Context) error { - api, closer, err := GetFullNodeAPI(cctx) - if err != nil { - return err - } - defer closer() - ctx := ReqContext(cctx) - if cctx.Args().Len() != 3 { return ShowHelp(cctx, fmt.Errorf("incorrect number of arguments")) } - to, err := address.NewFromString(cctx.Args().First()) - if err != nil { - return xerrors.Errorf("parsing toAddr: %w", err) - } - method, err := strconv.ParseInt(cctx.Args().Get(1), 10, 64) if err != nil { return xerrors.Errorf("parsing method id: %w", err) } - ts, err := LoadTipSet(ctx, cctx, api) - if err != nil { - return err - } + ctx := ReqContext(cctx) - act, err := api.StateGetActor(ctx, to, ts.Key()) - if err != nil { - return xerrors.Errorf("getting actor: %w", err) - } + var p []byte + if !cctx.Bool("to-code") { + svc, err := GetFullNodeServices(cctx) + if err != nil { + return err + } + defer svc.Close() // nolint - methodMeta, found := stmgr.MethodsMap[act.Code][abi.MethodNum(method)] - if !found { - return fmt.Errorf("method %d not found on actor %s", method, act.Code) - } + to, err := address.NewFromString(cctx.Args().First()) + if err != nil { + return xerrors.Errorf("parsing to addr: %w", err) + } - p := reflect.New(methodMeta.Params.Elem()).Interface().(cbg.CBORMarshaler) + p, err = svc.DecodeTypedParamsFromJSON(ctx, to, abi.MethodNum(method), cctx.Args().Get(2)) + if err != nil { + return xerrors.Errorf("decoding json params: %w", err) + } + } else { + api, done, err := GetFullNodeAPIV1(cctx) + if err != nil { + return err + } + defer done() - if err := json.Unmarshal([]byte(cctx.Args().Get(2)), p); err != nil { - return fmt.Errorf("unmarshaling input into params type: %w", err) - } + to, err := cid.Parse(cctx.Args().First()) + if err != nil { + return xerrors.Errorf("parsing to addr: %w", err) + } - buf := new(bytes.Buffer) - if err := p.MarshalCBOR(buf); err != nil { - return err + p, err = api.StateEncodeParams(ctx, to, abi.MethodNum(method), json.RawMessage(cctx.Args().Get(2))) + if err != nil { + return xerrors.Errorf("decoding json params: %w", err) + } } switch cctx.String("encoding") { - case "base64": - fmt.Println(base64.StdEncoding.EncodeToString(buf.Bytes())) + case "base64", "b64": + fmt.Println(base64.StdEncoding.EncodeToString(p)) case "hex": - fmt.Println(hex.EncodeToString(buf.Bytes())) + fmt.Println(hex.EncodeToString(p)) default: - return xerrors.Errorf("unrecognized encoding: %s", cctx.String("encoding")) + return xerrors.Errorf("unknown encoding") } return nil diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index 15c998d5b55..72a9becbaa5 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -164,6 +164,7 @@ * [StateCompute](#StateCompute) * [StateDealProviderCollateralBounds](#StateDealProviderCollateralBounds) * [StateDecodeParams](#StateDecodeParams) + * [StateEncodeParams](#StateEncodeParams) * [StateGetActor](#StateGetActor) * [StateListActors](#StateListActors) * [StateListMessages](#StateListMessages) @@ -4184,6 +4185,25 @@ Inputs: Response: `{}` +### StateEncodeParams +StateEncodeParams attempts to encode the provided json params to the binary from + + +Perms: read + +Inputs: +```json +[ + { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + 1, + null +] +``` + +Response: `"Ynl0ZSBhcnJheQ=="` + ### StateGetActor StateGetActor returns the indicated actor's nonce and balance. diff --git a/documentation/en/cli-lotus.md b/documentation/en/cli-lotus.md index 75f329a0f4f..b8cf6230af7 100644 --- a/documentation/en/cli-lotus.md +++ b/documentation/en/cli-lotus.md @@ -2297,11 +2297,12 @@ NAME: lotus chain encode params - Encodes the given JSON params USAGE: - lotus chain encode params [command options] [toAddr method params] + lotus chain encode params [command options] [dest method params] OPTIONS: --tipset value --encoding value specify input encoding to parse (default: "base64") + --to-code interpret dest as code CID instead of as address (default: false) --help, -h show help (default: false) ``` diff --git a/node/impl/full/state.go b/node/impl/full/state.go index df08887873c..ba441c6cdd9 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -3,8 +3,10 @@ package full import ( "bytes" "context" + "encoding/json" "strconv" + "github.com/filecoin-project/go-state-types/cbor" cid "github.com/ipfs/go-cid" "go.uber.org/fx" "golang.org/x/xerrors" @@ -497,6 +499,24 @@ func (a *StateAPI) StateDecodeParams(ctx context.Context, toAddr address.Address return paramType, nil } +func (a *StateAPI) StateEncodeParams(ctx context.Context, toActCode cid.Cid, method abi.MethodNum, params json.RawMessage) ([]byte, error) { + paramType, err := stmgr.GetParamType(toActCode, method) + if err != nil { + return nil, xerrors.Errorf("getting params type: %w", err) + } + + if err := json.Unmarshal(params, ¶mType); err != nil { + return nil, xerrors.Errorf("json unmarshal: %w", err) + } + + var cbb bytes.Buffer + if err := paramType.(cbor.Marshaler).MarshalCBOR(&cbb); err != nil { + return nil, xerrors.Errorf("cbor marshal: %w", err) + } + + return cbb.Bytes(), 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) { // XXX: Gets the state by computing the tipset state, instead of looking at the parent.