diff --git a/api/api_full.go b/api/api_full.go index 398df3ad1..0649ececf 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 80def1c97..a6e0e9e91 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 51478da0e..fd521a2b5 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 846a5a0c7..ba509bb51 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 276df8c73..69585cbd4 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 fbcaec0db..bedd7b120 100644 Binary files a/build/openrpc/worker.json.gz and b/build/openrpc/worker.json.gz differ diff --git a/cmd/lotus-shed/msg.go b/cmd/lotus-shed/msg.go index 63cfc86b9..c0c5c8eb1 100644 --- a/cmd/lotus-shed/msg.go +++ b/cmd/lotus-shed/msg.go @@ -14,6 +14,7 @@ import ( "golang.org/x/xerrors" "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/lotus/chain/stmgr" @@ -23,7 +24,11 @@ import ( ) var msgCmd = &cli.Command{ - Name: "msg", + Name: "msg", + Subcommands: []*cli.Command{ + msgParamsCmd, + msgActorParamsCmd, + }, Usage: "Translate message between various formats", ArgsUsage: "Message in any form", Action: func(cctx *cli.Context) error { @@ -278,3 +283,113 @@ func messageFromCID(cctx *cli.Context, c cid.Cid) (types.ChainMsg, error) { return messageFromBytes(cctx, msgb) } + +var msgParamsCmd = &cli.Command{ + Name: "encode-params", + Usage: "convert json params to binary (for given dest address)", + ArgsUsage: "[json params]", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "to", + Usage: "dest address", + Required: true, + }, + &cli.Int64Flag{ + Name: "method", + Aliases: []string{"m"}, + Usage: "method number", + Required: true, + }, + &cli.StringFlag{ + Name: "encoding", + Aliases: []string{"e"}, + Usage: "out encoding (b64, hex)", + Value: "b64", + }, + }, + Action: func(cctx *cli.Context) error { + svc, err := lcli.GetFullNodeServices(cctx) + if err != nil { + return err + } + defer svc.Close() // nolint + + ctx := lcli.ReqContext(cctx) + + to, err := address.NewFromString(cctx.String("to")) + if err != nil { + return xerrors.Errorf("parsing to addr: %w", err) + } + + p, err := svc.DecodeTypedParamsFromJSON(ctx, to, abi.MethodNum(cctx.Int64("method")), cctx.Args().First()) + if err != nil { + return xerrors.Errorf("decoding json params: %w", err) + } + + switch cctx.String("encoding") { + case "b64": + fmt.Println(base64.StdEncoding.EncodeToString(p)) + case "hex": + fmt.Println(hex.EncodeToString(p)) + default: + return xerrors.Errorf("unknown encoding") + } + + return nil + }, +} + +var msgActorParamsCmd = &cli.Command{ + Name: "encode-params-code", + Usage: "convert json params to binary (for given actor code)", + ArgsUsage: "[json params]", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "to", + Usage: "dest actor code cid", + Required: true, + }, + &cli.Int64Flag{ + Name: "method", + Aliases: []string{"m"}, + Usage: "method number", + Required: true, + }, + &cli.StringFlag{ + Name: "encoding", + Aliases: []string{"e"}, + Usage: "out encoding (b64, hex)", + Value: "b64", + }, + }, + Action: func(cctx *cli.Context) error { + api, done, err := lcli.GetFullNodeAPIV1(cctx) + if err != nil { + return err + } + defer done() + + ctx := lcli.ReqContext(cctx) + + to, err := cid.Parse(cctx.String("to")) + if err != nil { + return xerrors.Errorf("parsing to addr: %w", err) + } + + p, err := api.StateEncodeParams(ctx, to, abi.MethodNum(cctx.Int64("method")), json.RawMessage(cctx.Args().First())) + if err != nil { + return xerrors.Errorf("decoding json params: %w", err) + } + + switch cctx.String("encoding") { + case "b64": + fmt.Println(base64.StdEncoding.EncodeToString(p)) + case "hex": + fmt.Println(hex.EncodeToString(p)) + default: + 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 15c998d5b..72a9becba 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/node/impl/full/state.go b/node/impl/full/state.go index df0888787..ba441c6cd 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.