From 7de8ef75b35b77b8a414ea89c746a176f5ff8a34 Mon Sep 17 00:00:00 2001 From: Aaron Craelius Date: Tue, 11 Aug 2020 03:19:49 -0400 Subject: [PATCH] Require proto.Message in client.Context.PrintOutput (#6999) * Enable proto JSON json for cli tx & query * WIP on tests * Test fixes, cleanup * Cleanup * Address review comments * Update client/context.go Co-authored-by: Anil Kumar Kammari * Fixes Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Co-authored-by: Anil Kumar Kammari --- client/context.go | 14 ++++++++- client/rpc/validators.go | 2 +- x/auth/client/cli/query.go | 10 ++---- x/bank/client/cli/cli_test.go | 44 +++++++++++++-------------- x/bank/client/cli/query.go | 4 +-- x/distribution/client/cli/cli_test.go | 12 +++++--- x/distribution/client/cli/query.go | 10 +++--- x/distribution/client/cli/tx.go | 2 +- x/distribution/client/cli/utils.go | 3 +- x/evidence/client/cli/query.go | 23 ++------------ x/gov/client/cli/query.go | 23 ++++++++------ x/ibc/02-client/client/cli/query.go | 9 +++--- x/ibc/04-channel/client/cli/query.go | 3 +- x/mint/client/cli/cli_test.go | 8 ++--- x/mint/client/cli/query.go | 7 +++-- x/params/client/cli/query.go | 2 +- x/slashing/client/cli/query.go | 4 +-- x/staking/client/cli/query.go | 12 ++++---- x/upgrade/client/cli/query.go | 2 +- 19 files changed, 95 insertions(+), 99 deletions(-) diff --git a/client/context.go b/client/context.go index 74f791e62..ade34161e 100644 --- a/client/context.go +++ b/client/context.go @@ -5,6 +5,8 @@ import ( "io" "os" + "github.com/gogo/protobuf/proto" + "github.com/pkg/errors" rpcclient "github.com/tendermint/tendermint/rpc/client" rpchttp "github.com/tendermint/tendermint/rpc/client/http" @@ -209,7 +211,17 @@ func (ctx Context) PrintString(str string) error { // PrintOutput outputs toPrint to the ctx.Output based on ctx.OutputFormat which is // either text or json. If text, toPrint will be YAML encoded. Otherwise, toPrint // will be JSON encoded using ctx.JSONMarshaler. An error is returned upon failure. -func (ctx Context) PrintOutput(toPrint interface{}) error { +func (ctx Context) PrintOutput(toPrint proto.Message) error { + return ctx.printOutput(toPrint) +} + +// PrintOutputLegacy is a variant of PrintOutput that doesn't require a proto type +// and uses amino JSON encoding. It will be removed in the near future! +func (ctx Context) PrintOutputLegacy(toPrint interface{}) error { + return ctx.WithJSONMarshaler(ctx.LegacyAmino).printOutput(toPrint) +} + +func (ctx Context) printOutput(toPrint interface{}) error { // always serialize JSON initially because proto json can't be directly YAML encoded out, err := ctx.JSONMarshaler.MarshalJSON(toPrint) if err != nil { diff --git a/client/rpc/validators.go b/client/rpc/validators.go index 8038ff923..481177247 100644 --- a/client/rpc/validators.go +++ b/client/rpc/validators.go @@ -50,7 +50,7 @@ func ValidatorCommand() *cobra.Command { return err } - return clientCtx.PrintOutput(result) + return clientCtx.PrintOutputLegacy(result) }, } diff --git a/x/auth/client/cli/query.go b/x/auth/client/cli/query.go index 1641873ca..d58a303ca 100644 --- a/x/auth/client/cli/query.go +++ b/x/auth/client/cli/query.go @@ -64,7 +64,7 @@ $ query auth params return err } - return clientCtx.PrintOutput(res.Params) + return clientCtx.PrintOutput(&res.Params) }, } @@ -98,13 +98,7 @@ func GetAccountCmd() *cobra.Command { return err } - var account types.AccountI - err = clientCtx.InterfaceRegistry.UnpackAny(res.Account, &account) - if err != nil { - return err - } - - return clientCtx.PrintOutput(account) + return clientCtx.PrintOutput(res.Account) }, } diff --git a/x/bank/client/cli/cli_test.go b/x/bank/client/cli/cli_test.go index ca4e32800..d8c1b8b4b 100644 --- a/x/bank/client/cli/cli_test.go +++ b/x/bank/client/cli/cli_test.go @@ -5,6 +5,8 @@ import ( "fmt" "testing" + "github.com/gogo/protobuf/proto" + "github.com/stretchr/testify/suite" tmcli "github.com/tendermint/tendermint/libs/cli" @@ -128,43 +130,41 @@ func (s *IntegrationTestSuite) TestGetCmdQueryTotalSupply() { name string args []string expectErr bool - respType fmt.Stringer - expected fmt.Stringer + respType proto.Message + expected proto.Message }{ { - "total supply", - []string{ + name: "total supply", + args: []string{ fmt.Sprintf("--%s=1", flags.FlagHeight), fmt.Sprintf("--%s=json", tmcli.OutputFlag), }, - false, - &sdk.Coins{}, - sdk.NewCoins( - sdk.NewCoin(fmt.Sprintf("%stoken", val.Moniker), s.cfg.AccountTokens), - sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Add(sdk.NewInt(10))), - ), + respType: &types.QueryTotalSupplyResponse{}, + expected: &types.QueryTotalSupplyResponse{ + Supply: sdk.NewCoins( + sdk.NewCoin(fmt.Sprintf("%stoken", val.Moniker), s.cfg.AccountTokens), + sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Add(sdk.NewInt(10))), + )}, }, { - "total supply of a specific denomination", - []string{ + name: "total supply of a specific denomination", + args: []string{ fmt.Sprintf("--%s=1", flags.FlagHeight), fmt.Sprintf("--%s=%s", cli.FlagDenom, s.cfg.BondDenom), fmt.Sprintf("--%s=json", tmcli.OutputFlag), }, - false, - &sdk.Coin{}, - sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Add(sdk.NewInt(10))), + respType: &sdk.Coin{}, + expected: &sdk.Coin{s.cfg.BondDenom, s.cfg.StakingTokens.Add(sdk.NewInt(10))}, }, { - "total supply of a bogus denom", - []string{ + name: "total supply of a bogus denom", + args: []string{ fmt.Sprintf("--%s=1", flags.FlagHeight), fmt.Sprintf("--%s=foobar", cli.FlagDenom), fmt.Sprintf("--%s=json", tmcli.OutputFlag), }, - false, - &sdk.Coin{}, - sdk.NewCoin("foobar", sdk.ZeroInt()), + respType: &sdk.Coin{}, + expected: &sdk.Coin{"foobar", sdk.ZeroInt()}, }, } @@ -188,8 +188,8 @@ func (s *IntegrationTestSuite) TestGetCmdQueryTotalSupply() { s.Require().Error(err) } else { s.Require().NoError(err) - s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) - s.Require().Equal(tc.expected.String(), tc.respType.String()) + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), tc.respType)) + s.Require().Equal(tc.expected, tc.respType) } }) } diff --git a/x/bank/client/cli/query.go b/x/bank/client/cli/query.go index fb00993bb..ad4cfcca1 100644 --- a/x/bank/client/cli/query.go +++ b/x/bank/client/cli/query.go @@ -140,7 +140,7 @@ To query for the total supply of a specific coin denomination use: return err } - return clientCtx.PrintOutput(res.Supply) + return clientCtx.PrintOutput(res) } res, err := queryClient.SupplyOf(context.Background(), &types.QuerySupplyOfRequest{Denom: denom}) @@ -148,7 +148,7 @@ To query for the total supply of a specific coin denomination use: return err } - return clientCtx.PrintOutput(res.Amount) + return clientCtx.PrintOutput(&res.Amount) }, } diff --git a/x/distribution/client/cli/cli_test.go b/x/distribution/client/cli/cli_test.go index 7aba70e57..9ac907dd4 100644 --- a/x/distribution/client/cli/cli_test.go +++ b/x/distribution/client/cli/cli_test.go @@ -48,7 +48,7 @@ func (s *IntegrationTestSuite) SetupTest() { mintData.Params.InflationMin = inflation mintData.Params.InflationMax = inflation - mintDataBz, err := cfg.Codec.MarshalJSON(mintData) + mintDataBz, err := cfg.Codec.MarshalJSON(&mintData) s.Require().NoError(err) genesisState[minttypes.ModuleName] = mintDataBz cfg.GenesisState = genesisState @@ -389,7 +389,7 @@ func (s *IntegrationTestSuite) TestGetCmdQueryDelegatorRewards() { fmt.Sprintf("--%s=json", tmcli.OutputFlag), }, false, - `[{"denom":"stake","amount":"387.100000000000000000"}]`, + `{"rewards":[{"denom":"stake","amount":"387.100000000000000000"}]}`, }, { "text output", @@ -416,7 +416,8 @@ total: addr.String(), valAddr.String(), }, false, - `- amount: "387.100000000000000000" + `rewards: +- amount: "387.100000000000000000" denom: stake`, }, } @@ -461,12 +462,13 @@ func (s *IntegrationTestSuite) TestGetCmdQueryCommunityPool() { { "json output", []string{fmt.Sprintf("--%s=3", flags.FlagHeight), fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, - `[{"denom":"stake","amount":"4.740000000000000000"}]`, + `{"pool":[{"denom":"stake","amount":"4.740000000000000000"}]}`, }, { "text output", []string{fmt.Sprintf("--%s=text", tmcli.OutputFlag), fmt.Sprintf("--%s=3", flags.FlagHeight)}, - `- amount: "4.740000000000000000" + `pool: +- amount: "4.740000000000000000" denom: stake`, }, } diff --git a/x/distribution/client/cli/query.go b/x/distribution/client/cli/query.go index 14138e1b8..d6bc622f1 100644 --- a/x/distribution/client/cli/query.go +++ b/x/distribution/client/cli/query.go @@ -56,7 +56,7 @@ func GetCmdQueryParams() *cobra.Command { return err } - return clientCtx.PrintOutput(res.GetParams()) + return clientCtx.PrintOutput(&res.Params) }, } @@ -101,7 +101,7 @@ $ %s query distribution validator-outstanding-rewards cosmosvaloper1lwjmdnks33xw return err } - return clientCtx.PrintOutput(res.GetRewards()) + return clientCtx.PrintOutput(&res.Rewards) }, } @@ -145,7 +145,7 @@ $ %s query distribution commission cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9l return err } - return clientCtx.PrintOutput(res.GetCommission()) + return clientCtx.PrintOutput(&res.Commission) }, } @@ -262,7 +262,7 @@ $ %s query distribution rewards cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p co return err } - return clientCtx.PrintOutput(res.GetRewards()) + return clientCtx.PrintOutput(res) } res, err := queryClient.DelegationTotalRewards( @@ -309,7 +309,7 @@ $ %s query distribution community-pool return err } - return clientCtx.PrintOutput(res.GetPool()) + return clientCtx.PrintOutput(res) }, } diff --git a/x/distribution/client/cli/tx.go b/x/distribution/client/cli/tx.go index 4df081a99..3842bb5bb 100644 --- a/x/distribution/client/cli/tx.go +++ b/x/distribution/client/cli/tx.go @@ -287,7 +287,7 @@ Where proposal.json contains: return err } - proposal, err := ParseCommunityPoolSpendProposalJSON(clientCtx.JSONMarshaler, args[0]) + proposal, err := ParseCommunityPoolSpendProposalJSON(clientCtx.LegacyAmino, args[0]) if err != nil { return err } diff --git a/x/distribution/client/cli/utils.go b/x/distribution/client/cli/utils.go index 078085c2a..f0b25ac47 100644 --- a/x/distribution/client/cli/utils.go +++ b/x/distribution/client/cli/utils.go @@ -19,7 +19,8 @@ type ( ) // ParseCommunityPoolSpendProposalJSON reads and parses a CommunityPoolSpendProposalJSON from a file. -func ParseCommunityPoolSpendProposalJSON(cdc codec.JSONMarshaler, proposalFile string) (CommunityPoolSpendProposalJSON, error) { +// TODO: migrate this to protobuf +func ParseCommunityPoolSpendProposalJSON(cdc *codec.LegacyAmino, proposalFile string) (CommunityPoolSpendProposalJSON, error) { proposal := CommunityPoolSpendProposalJSON{} contents, err := ioutil.ReadFile(proposalFile) diff --git a/x/evidence/client/cli/query.go b/x/evidence/client/cli/query.go index db9fd0da1..313656485 100644 --- a/x/evidence/client/cli/query.go +++ b/x/evidence/client/cli/query.go @@ -12,7 +12,6 @@ import ( "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/types/query" "github.com/cosmos/cosmos-sdk/version" - "github.com/cosmos/cosmos-sdk/x/evidence/exported" "github.com/cosmos/cosmos-sdk/x/evidence/types" ) @@ -86,13 +85,7 @@ func queryEvidence(clientCtx client.Context, hash string) error { return err } - var evidence exported.Evidence - err = clientCtx.InterfaceRegistry.UnpackAny(res.Evidence, &evidence) - if err != nil { - return err - } - - return clientCtx.PrintOutput(evidence) + return clientCtx.PrintOutput(res.Evidence) } func queryAllEvidence(clientCtx client.Context, pageReq *query.PageRequest) error { @@ -103,21 +96,9 @@ func queryAllEvidence(clientCtx client.Context, pageReq *query.PageRequest) erro } res, err := queryClient.AllEvidence(context.Background(), params) - if err != nil { return err } - evidence := make([]exported.Evidence, 0, len(res.Evidence)) - for _, eviAny := range res.Evidence { - var evi exported.Evidence - err = clientCtx.InterfaceRegistry.UnpackAny(eviAny, &evi) - if err != nil { - return err - } - - evidence = append(evidence, evi) - } - - return clientCtx.PrintOutput(evidence) + return clientCtx.PrintOutput(res) } diff --git a/x/gov/client/cli/query.go b/x/gov/client/cli/query.go index 095cfa5fd..48af66a62 100644 --- a/x/gov/client/cli/query.go +++ b/x/gov/client/cli/query.go @@ -82,7 +82,7 @@ $ %s query gov proposal 1 return err } - return clientCtx.PrintOutput(res.GetProposal()) + return clientCtx.PrintOutput(&res.Proposal) }, } @@ -237,7 +237,7 @@ $ %s query gov vote 1 cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk } } - return clientCtx.PrintOutput(res.GetVote()) + return clientCtx.PrintOutput(&res.Vote) }, } @@ -298,7 +298,7 @@ $ %[1]s query gov votes 1 --page=2 --limit=100 var votes types.Votes clientCtx.JSONMarshaler.MustUnmarshalJSON(resByTxQuery, &votes) - return clientCtx.PrintOutput(votes) + return clientCtx.PrintOutputLegacy(votes) } @@ -389,7 +389,7 @@ $ %s query gov deposit 1 cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk clientCtx.JSONMarshaler.MustUnmarshalJSON(resByTxQuery, &deposit) } - return clientCtx.PrintOutput(deposit) + return clientCtx.PrintOutput(&deposit) }, } @@ -447,7 +447,8 @@ $ %s query gov deposits 1 var dep types.Deposits clientCtx.JSONMarshaler.MustUnmarshalJSON(resByTxQuery, &dep) - return clientCtx.PrintOutput(dep) + + return clientCtx.PrintOutputLegacy(dep) } pageReq, err := client.ReadPageRequest(cmd.Flags()) @@ -522,7 +523,7 @@ $ %s query gov tally 1 return err } - return clientCtx.PrintOutput(res.GetTally()) + return clientCtx.PrintOutput(&res.Tally) }, } @@ -579,11 +580,13 @@ $ %s query gov params return err } - return clientCtx.PrintOutput(types.NewParams( + params := types.NewParams( votingRes.GetVotingParams(), tallyRes.GetTallyParams(), depositRes.GetDepositParams(), - )) + ) + + return clientCtx.PrintOutputLegacy(params) }, } @@ -638,7 +641,7 @@ $ %s query gov param deposit return fmt.Errorf("argument must be one of (voting|tallying|deposit), was %s", args[0]) } - return clientCtx.PrintOutput(out) + return clientCtx.PrintOutputLegacy(out) }, } @@ -680,7 +683,7 @@ $ %s query gov proposer 1 return err } - return clientCtx.PrintOutput(prop) + return clientCtx.PrintOutputLegacy(prop) }, } diff --git a/x/ibc/02-client/client/cli/query.go b/x/ibc/02-client/client/cli/query.go index 17fb9e7e8..d59e43a55 100644 --- a/x/ibc/02-client/client/cli/query.go +++ b/x/ibc/02-client/client/cli/query.go @@ -41,7 +41,8 @@ func GetCmdQueryClientStates() *cobra.Command { } clientCtx = clientCtx.WithHeight(height) - return clientCtx.PrintOutput(clientStates) + + return clientCtx.PrintOutputLegacy(clientStates) }, } @@ -81,7 +82,7 @@ func GetCmdQueryClientState() *cobra.Command { } clientCtx = clientCtx.WithHeight(int64(clientStateRes.ProofHeight)) - return clientCtx.PrintOutput(clientStateRes) + return clientCtx.PrintOutputLegacy(clientStateRes) }, } @@ -125,7 +126,7 @@ func GetCmdQueryConsensusState() *cobra.Command { } clientCtx = clientCtx.WithHeight(int64(csRes.ProofHeight)) - return clientCtx.PrintOutput(csRes) + return clientCtx.PrintOutputLegacy(csRes) }, } @@ -155,7 +156,7 @@ func GetCmdQueryHeader() *cobra.Command { } clientCtx = clientCtx.WithHeight(height) - return clientCtx.PrintOutput(header) + return clientCtx.PrintOutputLegacy(header) }, } diff --git a/x/ibc/04-channel/client/cli/query.go b/x/ibc/04-channel/client/cli/query.go index 5cb4def99..71c9bb122 100644 --- a/x/ibc/04-channel/client/cli/query.go +++ b/x/ibc/04-channel/client/cli/query.go @@ -165,7 +165,8 @@ func GetCmdQueryChannelClientState() *cobra.Command { } clientCtx = clientCtx.WithHeight(height) - return clientCtx.PrintOutput(clientStateRes) + + return clientCtx.PrintOutputLegacy(clientStateRes) }, } diff --git a/x/mint/client/cli/cli_test.go b/x/mint/client/cli/cli_test.go index a517e4121..12fda75b5 100644 --- a/x/mint/client/cli/cli_test.go +++ b/x/mint/client/cli/cli_test.go @@ -114,12 +114,12 @@ func (s *IntegrationTestSuite) TestGetCmdQueryInflation() { { "json output", []string{fmt.Sprintf("--%s=1", flags.FlagHeight), fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, - `"1.000000000000000000"`, + `1.000000000000000000`, }, { "text output", []string{fmt.Sprintf("--%s=1", flags.FlagHeight), fmt.Sprintf("--%s=text", tmcli.OutputFlag)}, - `"1.000000000000000000"`, + `1.000000000000000000`, }, } @@ -155,12 +155,12 @@ func (s *IntegrationTestSuite) TestGetCmdQueryAnnualProvisions() { { "json output", []string{fmt.Sprintf("--%s=1", flags.FlagHeight), fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, - `"500000000.000000000000000000"`, + `500000000.000000000000000000`, }, { "text output", []string{fmt.Sprintf("--%s=1", flags.FlagHeight), fmt.Sprintf("--%s=text", tmcli.OutputFlag)}, - `"500000000.000000000000000000"`, + `500000000.000000000000000000`, }, } diff --git a/x/mint/client/cli/query.go b/x/mint/client/cli/query.go index 7390cbce3..d3a03ea23 100644 --- a/x/mint/client/cli/query.go +++ b/x/mint/client/cli/query.go @@ -2,6 +2,7 @@ package cli import ( "context" + "fmt" "github.com/spf13/cobra" @@ -52,7 +53,7 @@ func GetCmdQueryParams() *cobra.Command { return err } - return clientCtx.PrintOutput(res.GetParams()) + return clientCtx.PrintOutput(&res.Params) }, } @@ -84,7 +85,7 @@ func GetCmdQueryInflation() *cobra.Command { return err } - return clientCtx.PrintOutput(res.Inflation) + return clientCtx.PrintString(fmt.Sprintf("%s\n", res.Inflation)) }, } @@ -116,7 +117,7 @@ func GetCmdQueryAnnualProvisions() *cobra.Command { return err } - return clientCtx.PrintOutput(res.AnnualProvisions) + return clientCtx.PrintString(fmt.Sprintf("%s\n", res.AnnualProvisions)) }, } diff --git a/x/params/client/cli/query.go b/x/params/client/cli/query.go index 924fcef96..44c5fe811 100644 --- a/x/params/client/cli/query.go +++ b/x/params/client/cli/query.go @@ -47,7 +47,7 @@ func NewQuerySubspaceParamsCmd() *cobra.Command { return err } - return clientCtx.PrintOutput(res.GetParam()) + return clientCtx.PrintOutput(&res.Param) }, } diff --git a/x/slashing/client/cli/query.go b/x/slashing/client/cli/query.go index c174616cf..6558b03cd 100644 --- a/x/slashing/client/cli/query.go +++ b/x/slashing/client/cli/query.go @@ -65,7 +65,7 @@ $ query slashing signing-info cosmosvalconspub1zcjduepqfhvwcmt7p06fvdge return err } - return clientCtx.PrintOutput(res.ValSigningInfo) + return clientCtx.PrintOutput(&res.ValSigningInfo) }, } @@ -139,7 +139,7 @@ $ query slashing params return err } - return clientCtx.PrintOutput(res.Params) + return clientCtx.PrintOutput(&res.Params) }, } diff --git a/x/staking/client/cli/query.go b/x/staking/client/cli/query.go index 295ea79cc..12a50a983 100644 --- a/x/staking/client/cli/query.go +++ b/x/staking/client/cli/query.go @@ -80,7 +80,7 @@ $ %s query staking validator cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhff return err } - return clientCtx.PrintOutput(res.Validator) + return clientCtx.PrintOutput(&res.Validator) }, } @@ -126,7 +126,7 @@ $ %s query staking validators validators = append(validators, validator) } - return clientCtx.PrintOutput(validators) + return clientCtx.PrintOutputLegacy(validators) }, } @@ -452,7 +452,7 @@ $ %s query staking unbonding-delegation cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld7 return err } - return clientCtx.PrintOutput(res.Unbond) + return clientCtx.PrintOutput(&res.Unbond) }, } @@ -567,7 +567,7 @@ $ %s query staking redelegation cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p co return err } - return clientCtx.PrintOutput(res.RedelegationResponses) + return clientCtx.PrintOutput(res) }, } @@ -705,7 +705,7 @@ $ %s query staking pool return err } - return clientCtx.PrintOutput(res.Pool) + return clientCtx.PrintOutput(&res.Pool) }, } @@ -743,7 +743,7 @@ $ %s query staking params return err } - return clientCtx.PrintOutput(res.Params) + return clientCtx.PrintOutput(&res.Params) }, } diff --git a/x/upgrade/client/cli/query.go b/x/upgrade/client/cli/query.go index bb7a12ad1..2f2723966 100644 --- a/x/upgrade/client/cli/query.go +++ b/x/upgrade/client/cli/query.go @@ -105,7 +105,7 @@ func GetAppliedPlanCmd() *cobra.Command { if err != nil { return err } - return clientCtx.PrintOutput(string(bz)) + return clientCtx.PrintString(fmt.Sprintf("%s\n", string(bz))) }, }