From 5c5bf7c49b4a222d0c5580886dcac07024aa54be Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 21 May 2019 15:32:48 +0200 Subject: [PATCH] Updates for upstream PR (community pool spend proposal) (#10) Related cosmos/cosmos-sdk#4329 --- .pending/features/gaiad/Update-Gaia-for-comm | 1 + app/app.go | 3 +- app/sim_test.go | 1 + cli_test/cli_test.go | 84 ++++++++++++++++++++ cli_test/test_helpers.go | 14 ++++ cmd/gaiacli/main.go | 5 +- go.mod | 2 +- go.sum | 2 + lcd_test/helpers_test.go | 48 ++++++++++- lcd_test/lcd_test.go | 37 +++++++++ 10 files changed, 192 insertions(+), 5 deletions(-) create mode 100644 .pending/features/gaiad/Update-Gaia-for-comm diff --git a/.pending/features/gaiad/Update-Gaia-for-comm b/.pending/features/gaiad/Update-Gaia-for-comm new file mode 100644 index 00000000..95e1ec14 --- /dev/null +++ b/.pending/features/gaiad/Update-Gaia-for-comm @@ -0,0 +1 @@ +Update Gaia for community pool spend proposals per Cosmos Hub governance proposal #7 "Activate the Community Pool" \ No newline at end of file diff --git a/app/app.go b/app/app.go index 1cd5e797..4d147bef 100644 --- a/app/app.go +++ b/app/app.go @@ -159,7 +159,8 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest b // register the proposal types govRouter := gov.NewRouter() govRouter.AddRoute(gov.RouterKey, gov.ProposalHandler). - AddRoute(params.RouterKey, params.NewParamChangeProposalHandler(app.paramsKeeper)) + AddRoute(params.RouterKey, params.NewParamChangeProposalHandler(app.paramsKeeper)). + AddRoute(distr.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.distrKeeper)) app.govKeeper = gov.NewKeeper(app.cdc, app.keyGov, app.paramsKeeper, govSubspace, app.bankKeeper, &stakingKeeper, gov.DefaultCodespace, govRouter) diff --git a/app/sim_test.go b/app/sim_test.go index be5a8ef3..4d52f86c 100644 --- a/app/sim_test.go +++ b/app/sim_test.go @@ -302,6 +302,7 @@ func testAndRunTxs(app *GaiaApp) []simulation.WeightedOperation { {50, distrsim.SimulateMsgWithdrawValidatorCommission(app.accountKeeper, app.distrKeeper)}, {5, govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, govsim.SimulateTextProposalContent)}, {5, govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, paramsim.SimulateParamChangeProposalContent)}, + {5, govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, distrsim.SimulateCommunityPoolSpendProposalContent(app.distrKeeper))}, {100, govsim.SimulateMsgDeposit(app.govKeeper)}, {100, stakingsim.SimulateMsgCreateValidator(app.accountKeeper, app.stakingKeeper)}, {5, stakingsim.SimulateMsgEditValidator(app.stakingKeeper)}, diff --git a/cli_test/cli_test.go b/cli_test/cli_test.go index 3da984e5..651a5b4d 100644 --- a/cli_test/cli_test.go +++ b/cli_test/cli_test.go @@ -687,6 +687,90 @@ func TestGaiaCLISubmitParamChangeProposal(t *testing.T) { f.Cleanup() } +func TestGaiaCLISubmitCommunityPoolSpendProposal(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // create some inflation + cdc := app.MakeCodec() + genesisState := f.GenesisState() + inflationMin := sdk.MustNewDecFromStr("10000.0") + var mintData mint.GenesisState + cdc.UnmarshalJSON(genesisState[mint.ModuleName], &mintData) + mintData.Minter.Inflation = inflationMin + mintData.Params.InflationMin = inflationMin + mintData.Params.InflationMax = sdk.MustNewDecFromStr("15000.0") + mintDataBz, err := cdc.MarshalJSON(mintData) + require.NoError(t, err) + genesisState[mint.ModuleName] = mintDataBz + + genFile := filepath.Join(f.GaiadHome, "config", "genesis.json") + genDoc, err := tmtypes.GenesisDocFromFile(genFile) + require.NoError(t, err) + genDoc.AppState, err = cdc.MarshalJSON(genesisState) + require.NoError(t, genDoc.SaveAs(genFile)) + + proc := f.GDStart() + defer proc.Stop(false) + + fooAddr := f.KeyAddress(keyFoo) + fooAcc := f.QueryAccount(fooAddr) + startTokens := sdk.TokensFromTendermintPower(50) + require.Equal(t, startTokens, fooAcc.GetCoins().AmountOf(sdk.DefaultBondDenom)) + + tests.WaitForNextNBlocksTM(3, f.Port) + + // write proposal to file + proposalTokens := sdk.TokensFromTendermintPower(5) + proposal := fmt.Sprintf(`{ + "title": "Community Pool Spend", + "description": "Spend from community pool", + "recipient": "%s", + "amount": [ + { + "denom": "%s", + "amount": "1" + } + ], + "deposit": [ + { + "denom": "%s", + "amount": "%s" + } + ] +} +`, fooAddr, sdk.DefaultBondDenom, sdk.DefaultBondDenom, proposalTokens.String()) + proposalFile := WriteToNewTempFile(t, proposal) + + // create the param change proposal + f.TxGovSubmitCommunityPoolSpendProposal(keyFoo, proposalFile.Name(), sdk.NewCoin(denom, proposalTokens), "-y") + tests.WaitForNextNBlocksTM(1, f.Port) + + // ensure transaction tags can be queried + txsPage := f.QueryTxs(1, 50, "action:submit_proposal", fmt.Sprintf("sender:%s", fooAddr)) + require.Len(t, txsPage.Txs, 1) + + // ensure deposit was deducted + fooAcc = f.QueryAccount(fooAddr) + require.Equal(t, startTokens.Sub(proposalTokens).String(), fooAcc.GetCoins().AmountOf(sdk.DefaultBondDenom).String()) + + // ensure proposal is directly queryable + proposal1 := f.QueryGovProposal(1) + require.Equal(t, uint64(1), proposal1.ProposalID) + require.Equal(t, gov.StatusDepositPeriod, proposal1.Status) + + // ensure correct query proposals result + proposalsQuery := f.QueryGovProposals() + require.Equal(t, uint64(1), proposalsQuery[0].ProposalID) + + // ensure the correct deposit amount on the proposal + deposit := f.QueryGovDeposit(1, fooAddr) + require.Equal(t, proposalTokens, deposit.Amount.AmountOf(denom)) + + // Cleanup testing directories + f.Cleanup() +} + func TestGaiaCLIQueryTxPagination(t *testing.T) { t.Parallel() f := InitFixtures(t) diff --git a/cli_test/test_helpers.go b/cli_test/test_helpers.go index c747241f..5b926a41 100644 --- a/cli_test/test_helpers.go +++ b/cli_test/test_helpers.go @@ -403,6 +403,20 @@ func (f *Fixtures) TxGovSubmitParamChangeProposal( return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass) } +// TxGovSubmitCommunityPoolSpendProposal executes a CLI community pool spend proposal +// submission. +func (f *Fixtures) TxGovSubmitCommunityPoolSpendProposal( + from, proposalPath string, deposit sdk.Coin, flags ...string, +) (bool, string, string) { + + cmd := fmt.Sprintf( + "%s tx gov submit-proposal community-pool-spend %s --from=%s %v", + f.GaiacliBinary, proposalPath, from, f.Flags(), + ) + + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass) +} + //___________________________________________________________________________________ // gaiacli query account diff --git a/cmd/gaiacli/main.go b/cmd/gaiacli/main.go index 8f77882d..0fd1ec10 100644 --- a/cmd/gaiacli/main.go +++ b/cmd/gaiacli/main.go @@ -23,6 +23,7 @@ import ( crisisclient "github.com/cosmos/cosmos-sdk/x/crisis/client" distcmd "github.com/cosmos/cosmos-sdk/x/distribution" distClient "github.com/cosmos/cosmos-sdk/x/distribution/client" + distrcli "github.com/cosmos/cosmos-sdk/x/distribution/client/cli" dist "github.com/cosmos/cosmos-sdk/x/distribution/client/rest" gv "github.com/cosmos/cosmos-sdk/x/gov" govClient "github.com/cosmos/cosmos-sdk/x/gov/client" @@ -70,7 +71,7 @@ func main() { // Module clients hold cli commnads (tx,query) and lcd routes // TODO: Make the lcd command take a list of ModuleClient mc := []sdk.ModuleClient{ - govClient.NewModuleClient(gv.StoreKey, cdc, paramcli.GetCmdSubmitProposal(cdc)), + govClient.NewModuleClient(gv.StoreKey, cdc, paramcli.GetCmdSubmitProposal(cdc), distrcli.GetCmdSubmitProposal(cdc)), distClient.NewModuleClient(distcmd.StoreKey, cdc), stakingclient.NewModuleClient(st.StoreKey, cdc), mintclient.NewModuleClient(mint.StoreKey, cdc), @@ -175,7 +176,7 @@ func registerRoutes(rs *lcd.RestServer) { dist.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, distcmd.StoreKey) staking.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) slashing.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) - gov.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, paramsrest.ProposalRESTHandler(rs.CliCtx, rs.Cdc)) + gov.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, paramsrest.ProposalRESTHandler(rs.CliCtx, rs.Cdc), dist.ProposalRESTHandler(rs.CliCtx, rs.Cdc)) mintrest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) } diff --git a/go.mod b/go.mod index 8d86b9c8..86a1494e 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.12 require ( github.com/btcsuite/btcd v0.0.0-20190427004231-96897255fd17 // indirect - github.com/cosmos/cosmos-sdk v0.28.2-0.20190520151953-4b872d2eb4bb + github.com/cosmos/cosmos-sdk v0.28.2-0.20190521100210-dd89c329516e github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d // indirect github.com/gogo/protobuf v1.2.1 // indirect github.com/google/gofuzz v1.0.0 // indirect diff --git a/go.sum b/go.sum index 0183bd7f..469b686a 100644 --- a/go.sum +++ b/go.sum @@ -39,6 +39,8 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/cosmos/cosmos-sdk v0.28.2-0.20190520151953-4b872d2eb4bb h1:njT3ur+sTKBaNvQOaWVvFr1mqczrqB3gaLd2BTCgXlQ= github.com/cosmos/cosmos-sdk v0.28.2-0.20190520151953-4b872d2eb4bb/go.mod h1:asKfALbDgAL4BGwQdoYwEyfRIPHL20mA2Esan1ELVB4= +github.com/cosmos/cosmos-sdk v0.28.2-0.20190521100210-dd89c329516e h1:sey/szRUq+qpsaWP3TxNzT8MmFMQT/6QFhqD6xeLF9o= +github.com/cosmos/cosmos-sdk v0.28.2-0.20190521100210-dd89c329516e/go.mod h1:asKfALbDgAL4BGwQdoYwEyfRIPHL20mA2Esan1ELVB4= github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8 h1:Iwin12wRQtyZhH6FV3ykFcdGNlYEzoeR0jN8Vn+JWsI= github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d h1:49RLWk1j44Xu4fjHb6JFYmeUnDORVwHNkDxaQ0ctCVU= diff --git a/lcd_test/helpers_test.go b/lcd_test/helpers_test.go index 7786a799..2ed87131 100644 --- a/lcd_test/helpers_test.go +++ b/lcd_test/helpers_test.go @@ -301,6 +301,14 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress accs = append(accs, acc) } + // distr data + distrDataBz := genesisState[distr.ModuleName] + var distrData distr.GenesisState + cdc.MustUnmarshalJSON(distrDataBz, &distrData) + distrData.FeePool.CommunityPool = sdk.DecCoins{sdk.DecCoin{"test", sdk.NewDecFromInt(sdk.NewInt(10))}} + distrDataBz = cdc.MustMarshalJSON(distrData) + genesisState[distr.ModuleName] = distrDataBz + // now add the account tokens to the non-bonded pool for _, acc := range accs { accTokens := acc.Coins.AmountOf(sdk.DefaultBondDenom) @@ -438,7 +446,7 @@ func registerRoutes(rs *lcd.RestServer) { distrrest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, distr.StoreKey) stakingrest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) slashingrest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) - govrest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, paramsrest.ProposalRESTHandler(rs.CliCtx, rs.Cdc)) + govrest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, paramsrest.ProposalRESTHandler(rs.CliCtx, rs.Cdc), distrrest.ProposalRESTHandler(rs.CliCtx, rs.Cdc)) mintrest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) } @@ -1221,6 +1229,44 @@ func doSubmitParamChangeProposal( return txResp } +func doSubmitCommunityPoolSpendProposal( + t *testing.T, port, seed, name, pwd string, proposerAddr sdk.AccAddress, + amount sdk.Int, fees sdk.Coins, +) sdk.TxResponse { + + acc := getAccount(t, port, proposerAddr) + accnum := acc.GetAccountNumber() + sequence := acc.GetSequence() + chainID := viper.GetString(client.FlagChainID) + from := acc.GetAddress().String() + + baseReq := rest.NewBaseReq(from, "", chainID, "", "", accnum, sequence, fees, nil, false) + pr := distrrest.CommunityPoolSpendProposalReq{ + BaseReq: baseReq, + Title: "Test", + Description: "test", + Proposer: proposerAddr, + Recipient: proposerAddr, + Deposit: sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, amount)}, + Amount: sdk.Coins{sdk.NewCoin("test", sdk.NewInt(5))}, + } + + req, err := cdc.MarshalJSON(pr) + require.NoError(t, err) + + resp, body := Request(t, port, "POST", "/gov/proposals/community_pool_spend", req) + require.Equal(t, http.StatusOK, resp.StatusCode, body) + + resp, body = signAndBroadcastGenTx(t, port, name, pwd, body, acc, client.DefaultGasAdjustment, false) + require.Equal(t, http.StatusOK, resp.StatusCode, body) + + var txResp sdk.TxResponse + err = cdc.UnmarshalJSON([]byte(body), &txResp) + require.NoError(t, err) + + return txResp +} + // GET /gov/proposals Query proposals func getProposalsAll(t *testing.T, port string) []gov.Proposal { res, body := Request(t, port, "GET", "/gov/proposals", nil) diff --git a/lcd_test/lcd_test.go b/lcd_test/lcd_test.go index 8a0ff0bf..af5105b6 100644 --- a/lcd_test/lcd_test.go +++ b/lcd_test/lcd_test.go @@ -625,6 +625,43 @@ func TestSubmitProposal(t *testing.T) { require.Equal(t, proposalID, proposer.ProposalID) } +func TestSubmitCommunityPoolSpendProposal(t *testing.T) { + kb, err := keys.NewKeyBaseFromDir(InitClientHome(t, "")) + require.NoError(t, err) + addr, seed := CreateAddr(t, name1, pw, kb) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}, true) + defer cleanup() + + acc := getAccount(t, port, addr) + initialBalance := acc.GetCoins() + + // create proposal tx + proposalTokens := sdk.TokensFromTendermintPower(5) + resultTx := doSubmitCommunityPoolSpendProposal(t, port, seed, name1, pw, addr, proposalTokens, fees) + tests.WaitForHeight(resultTx.Height+1, port) + + // check if tx was committed + require.Equal(t, uint32(0), resultTx.Code) + + var proposalID uint64 + bz, err := hex.DecodeString(resultTx.Data) + require.NoError(t, err) + cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposalID) + + // verify balance + acc = getAccount(t, port, addr) + expectedBalance := initialBalance[0].Sub(fees[0]) + require.Equal(t, expectedBalance.Amount.Sub(proposalTokens), acc.GetCoins().AmountOf(sdk.DefaultBondDenom)) + + // query proposal + proposal := getProposal(t, port, proposalID) + require.Equal(t, "Test", proposal.GetTitle()) + + proposer := getProposer(t, port, proposalID) + require.Equal(t, addr.String(), proposer.Proposer) + require.Equal(t, proposalID, proposer.ProposalID) +} + func TestSubmitParamChangeProposal(t *testing.T) { kb, err := keys.NewKeyBaseFromDir(InitClientHome(t, "")) require.NoError(t, err)