From 4effa6f8d4f8a09586d09ef38c40ee5dccbddf20 Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Wed, 27 Jun 2018 16:49:59 -0700 Subject: [PATCH 1/3] Merge PR #1357: RESTful governance endpoints * get deposit rest endpoint * query proposals * changelog * fixed commented out headers * fixed undeterministic tests * increase circle test timeout * MustBech32ifyAcc * asdf --- .circleci/config.yml | 2 +- CHANGELOG.md | 1 + client/lcd/lcd_test.go | 144 +++++++++++++++++++++-- x/gov/client/rest/rest.go | 241 ++++++++++++++++++++++++++++++++++---- x/gov/depositsvotes.go | 26 +++- x/gov/keeper.go | 8 +- 6 files changed, 380 insertions(+), 42 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ce01e3971..bb1ad1671 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -119,7 +119,7 @@ jobs: for pkg in $(go list github.com/cosmos/cosmos-sdk/... | grep -v /vendor/ | grep -v github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test | circleci tests split --split-by=timings); do id=$(basename "$pkg") - go test -timeout 5m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg" + go test -timeout 8m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg" done - persist_to_workspace: root: /tmp/workspace diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a2ed1de3..70d3c303a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ BREAKING CHANGES * Removed MsgChangePubKey from auth * Removed setPubKey from account mapper * Removed GetMemo from Tx (it is still on StdTx) +* Gov module REST endpoints changed to be more RESTful * [cli] rearranged commands under subcommands * [stake] remove Tick and add EndBlocker * [stake] introduce concept of unbonding for delegations and validators diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 40bc77b7e..07bb4b252 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -475,6 +475,10 @@ func TestDeposit(t *testing.T) { // query proposal proposal = getProposal(t, port, proposalID) assert.True(t, proposal.TotalDeposit.IsEqual(sdk.Coins{sdk.NewCoin("steak", 10)})) + + // query deposit + deposit := getDeposit(t, port, proposalID, addr) + assert.True(t, deposit.Amount.IsEqual(sdk.Coins{sdk.NewCoin("steak", 10)})) } func TestVote(t *testing.T) { @@ -515,6 +519,75 @@ func TestVote(t *testing.T) { assert.Equal(t, gov.VoteOptionToString(gov.OptionYes), vote.Option) } +func TestProposalsQuery(t *testing.T) { + name, password := "test", "1234567890" + name2, password := "test2", "1234567890" + addr, seed := CreateAddr(t, "test", password, GetKB(t)) + addr2, seed2 := CreateAddr(t, "test2", password, GetKB(t)) + cleanup, _, port := InitializeTestLCD(t, 1, []sdk.Address{addr, addr2}) + defer cleanup() + + // Addr1 proposes (and deposits) proposals #1 and #2 + resultTx := doSubmitProposal(t, port, seed, name, password, addr) + var proposalID1 int64 + cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID1) + tests.WaitForHeight(resultTx.Height+1, port) + resultTx = doSubmitProposal(t, port, seed, name, password, addr) + var proposalID2 int64 + cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID2) + tests.WaitForHeight(resultTx.Height+1, port) + + // Addr2 proposes (and deposits) proposals #3 + resultTx = doSubmitProposal(t, port, seed2, name2, password, addr2) + var proposalID3 int64 + cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID3) + tests.WaitForHeight(resultTx.Height+1, port) + + // Addr2 deposits on proposals #2 & #3 + resultTx = doDeposit(t, port, seed2, name2, password, addr2, proposalID2) + tests.WaitForHeight(resultTx.Height+1, port) + resultTx = doDeposit(t, port, seed2, name2, password, addr2, proposalID3) + tests.WaitForHeight(resultTx.Height+1, port) + + // Addr1 votes on proposals #2 & #3 + resultTx = doVote(t, port, seed, name, password, addr, proposalID2) + tests.WaitForHeight(resultTx.Height+1, port) + resultTx = doVote(t, port, seed, name, password, addr, proposalID3) + tests.WaitForHeight(resultTx.Height+1, port) + + // Addr2 votes on proposal #3 + resultTx = doVote(t, port, seed2, name2, password, addr2, proposalID3) + tests.WaitForHeight(resultTx.Height+1, port) + + // Test query all proposals + proposals := getProposalsAll(t, port) + assert.Equal(t, proposalID1, (proposals[0]).ProposalID) + assert.Equal(t, proposalID2, (proposals[1]).ProposalID) + assert.Equal(t, proposalID3, (proposals[2]).ProposalID) + + // Test query deposited by addr1 + proposals = getProposalsFilterDepositer(t, port, addr) + assert.Equal(t, proposalID1, (proposals[0]).ProposalID) + + // Test query deposited by addr2 + proposals = getProposalsFilterDepositer(t, port, addr2) + assert.Equal(t, proposalID2, (proposals[0]).ProposalID) + assert.Equal(t, proposalID3, (proposals[1]).ProposalID) + + // Test query voted by addr1 + proposals = getProposalsFilterVoter(t, port, addr) + assert.Equal(t, proposalID2, (proposals[0]).ProposalID) + assert.Equal(t, proposalID3, (proposals[1]).ProposalID) + + // Test query voted by addr2 + proposals = getProposalsFilterVoter(t, port, addr2) + assert.Equal(t, proposalID3, (proposals[0]).ProposalID) + + // Test query voted and deposited by addr1 + proposals = getProposalsFilterVoterDepositer(t, port, addr, addr) + assert.Equal(t, proposalID2, (proposals[0]).ProposalID) +} + //_____________________________________________________________________________ // get the account to get the sequence func getAccount(t *testing.T, port string, addr sdk.Address) auth.Account { @@ -766,9 +839,19 @@ func getProposal(t *testing.T, port string, proposalID int64) gov.ProposalRest { return proposal } +func getDeposit(t *testing.T, port string, proposalID int64, depositerAddr sdk.Address) gov.DepositRest { + bechDepositerAddr := sdk.MustBech32ifyAcc(depositerAddr) + res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/deposits/%s", proposalID, bechDepositerAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + var deposit gov.DepositRest + err := cdc.UnmarshalJSON([]byte(body), &deposit) + require.Nil(t, err) + return deposit +} + func getVote(t *testing.T, port string, proposalID int64, voterAddr sdk.Address) gov.VoteRest { bechVoterAddr := sdk.MustBech32ifyAcc(voterAddr) - res, body := Request(t, port, "GET", fmt.Sprintf("/gov/votes/%d/%s", proposalID, bechVoterAddr), nil) + res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/votes/%s", proposalID, bechVoterAddr), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) var vote gov.VoteRest err := cdc.UnmarshalJSON([]byte(body), &vote) @@ -776,6 +859,53 @@ func getVote(t *testing.T, port string, proposalID int64, voterAddr sdk.Address) return vote } +func getProposalsAll(t *testing.T, port string) []gov.ProposalRest { + res, body := Request(t, port, "GET", "/gov/proposals", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var proposals []gov.ProposalRest + err := cdc.UnmarshalJSON([]byte(body), &proposals) + require.Nil(t, err) + return proposals +} + +func getProposalsFilterDepositer(t *testing.T, port string, depositerAddr sdk.Address) []gov.ProposalRest { + bechDepositerAddr := sdk.MustBech32ifyAcc(depositerAddr) + + res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals?depositer=%s", bechDepositerAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var proposals []gov.ProposalRest + err := cdc.UnmarshalJSON([]byte(body), &proposals) + require.Nil(t, err) + return proposals +} + +func getProposalsFilterVoter(t *testing.T, port string, voterAddr sdk.Address) []gov.ProposalRest { + bechVoterAddr := sdk.MustBech32ifyAcc(voterAddr) + + res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals?voter=%s", bechVoterAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var proposals []gov.ProposalRest + err := cdc.UnmarshalJSON([]byte(body), &proposals) + require.Nil(t, err) + return proposals +} + +func getProposalsFilterVoterDepositer(t *testing.T, port string, voterAddr sdk.Address, depositerAddr sdk.Address) []gov.ProposalRest { + bechVoterAddr := sdk.MustBech32ifyAcc(voterAddr) + bechDepositerAddr := sdk.MustBech32ifyAcc(depositerAddr) + + res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals?depositer=%s&voter=%s", bechDepositerAddr, bechVoterAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var proposals []gov.ProposalRest + err := cdc.UnmarshalJSON([]byte(body), &proposals) + require.Nil(t, err) + return proposals +} + func doSubmitProposal(t *testing.T, port, seed, name, password string, proposerAddr sdk.Address) (resultTx ctypes.ResultBroadcastTxCommit) { // get the account to get the sequence acc := getAccount(t, port, proposerAddr) @@ -802,7 +932,7 @@ func doSubmitProposal(t *testing.T, port, seed, name, password string, proposerA "gas": 100000 } }`, bechProposerAddr, name, password, chainID, accnum, sequence)) - res, body := Request(t, port, "POST", "/gov/submitproposal", jsonStr) + res, body := Request(t, port, "POST", "/gov/proposals", jsonStr) fmt.Println(res) require.Equal(t, http.StatusOK, res.StatusCode, body) @@ -826,7 +956,6 @@ func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk // deposit on proposal jsonStr := []byte(fmt.Sprintf(`{ "depositer": "%s", - "proposalID": %d, "amount": [{ "denom": "steak", "amount": 5 }], "base_req": { "name": "%s", @@ -836,8 +965,8 @@ func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk "sequence": %d, "gas": 100000 } - }`, bechProposerAddr, proposalID, name, password, chainID, accnum, sequence)) - res, body := Request(t, port, "POST", "/gov/deposit", jsonStr) + }`, bechProposerAddr, name, password, chainID, accnum, sequence)) + res, body := Request(t, port, "POST", fmt.Sprintf("/gov/proposals/%d/deposits", proposalID), jsonStr) fmt.Println(res) require.Equal(t, http.StatusOK, res.StatusCode, body) @@ -861,7 +990,6 @@ func doVote(t *testing.T, port, seed, name, password string, proposerAddr sdk.Ad // vote on proposal jsonStr := []byte(fmt.Sprintf(`{ "voter": "%s", - "proposalID": %d, "option": "Yes", "base_req": { "name": "%s", @@ -871,8 +999,8 @@ func doVote(t *testing.T, port, seed, name, password string, proposerAddr sdk.Ad "sequence": %d, "gas": 100000 } - }`, bechProposerAddr, proposalID, name, password, chainID, accnum, sequence)) - res, body := Request(t, port, "POST", "/gov/vote", jsonStr) + }`, bechProposerAddr, name, password, chainID, accnum, sequence)) + res, body := Request(t, port, "POST", fmt.Sprintf("/gov/proposals/%d/votes", proposalID), jsonStr) fmt.Println(res) require.Equal(t, http.StatusOK, res.StatusCode, body) diff --git a/x/gov/client/rest/rest.go b/x/gov/client/rest/rest.go index 7b53e5bf1..f065ca616 100644 --- a/x/gov/client/rest/rest.go +++ b/x/gov/client/rest/rest.go @@ -17,17 +17,22 @@ import ( // REST Variable names // nolint const ( - ProposalRestID = "proposalID" - RestVoter = "voterAddress" + RestProposalID = "proposalID" + RestDepositer = "depositer" + RestVoter = "voter" ) // RegisterRoutes - Central function to define routes that get registered by the main application func RegisterRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec, kb keys.Keybase) { - r.HandleFunc("/gov/submitproposal", postProposalHandlerFn(cdc, kb, ctx)).Methods("POST") - r.HandleFunc("/gov/deposit", depositHandlerFn(cdc, kb, ctx)).Methods("POST") - r.HandleFunc("/gov/vote", voteHandlerFn(cdc, kb, ctx)).Methods("POST") - r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}", ProposalRestID), queryProposalHandlerFn("gov", cdc, kb, ctx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/gov/votes/{%s}/{%s}", ProposalRestID, RestVoter), queryVoteHandlerFn("gov", cdc, kb, ctx)).Methods("GET") + r.HandleFunc("/gov/proposals", postProposalHandlerFn(cdc, kb, ctx)).Methods("POST") + r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits", RestProposalID), depositHandlerFn(cdc, kb, ctx)).Methods("POST") + r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/votes", RestProposalID), voteHandlerFn(cdc, kb, ctx)).Methods("POST") + + r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}", RestProposalID), queryProposalHandlerFn("gov", cdc, kb, ctx)).Methods("GET") + r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits/{%s}", RestProposalID, RestDepositer), queryDepositHandlerFn("gov", cdc, kb, ctx)).Methods("GET") + r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/votes/{%s}", RestProposalID, RestVoter), queryVoteHandlerFn("gov", cdc, kb, ctx)).Methods("GET") + + r.HandleFunc("/gov/proposals", queryProposalsWithParameterFn("gov", cdc, kb, ctx)).Methods("GET") } type postProposalReq struct { @@ -40,17 +45,15 @@ type postProposalReq struct { } type depositReq struct { - BaseReq baseReq `json:"base_req"` - ProposalID int64 `json:"proposalID"` // ID of the proposal - Depositer string `json:"depositer"` // Address of the depositer - Amount sdk.Coins `json:"amount"` // Coins to add to the proposal's deposit + BaseReq baseReq `json:"base_req"` + Depositer string `json:"depositer"` // Address of the depositer + Amount sdk.Coins `json:"amount"` // Coins to add to the proposal's deposit } type voteReq struct { - BaseReq baseReq `json:"base_req"` - Voter string `json:"voter"` // address of the voter - ProposalID int64 `json:"proposalID"` // proposalID of the proposal - Option string `json:"option"` // option from OptionSet chosen by the voter + BaseReq baseReq `json:"base_req"` + Voter string `json:"voter"` // address of the voter + Option string `json:"option"` // option from OptionSet chosen by the voter } func postProposalHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.CoreContext) http.HandlerFunc { @@ -92,8 +95,25 @@ func postProposalHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.CoreCon func depositHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.CoreContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + strProposalID := vars[RestProposalID] + + if len(strProposalID) == 0 { + w.WriteHeader(http.StatusBadRequest) + err := errors.New("proposalId required but not specified") + w.Write([]byte(err.Error())) + return + } + + proposalID, err := strconv.ParseInt(strProposalID, 10, 64) + if err != nil { + err := errors.Errorf("proposalID [%d] is not positive", proposalID) + w.Write([]byte(err.Error())) + return + } + var req depositReq - err := buildReq(w, r, &req) + err = buildReq(w, r, &req) if err != nil { return } @@ -109,7 +129,7 @@ func depositHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.CoreContext) } // create the message - msg := gov.NewMsgDeposit(depositer, req.ProposalID, req.Amount) + msg := gov.NewMsgDeposit(depositer, proposalID, req.Amount) err = msg.ValidateBasic() if err != nil { writeErr(&w, http.StatusBadRequest, err.Error()) @@ -123,8 +143,25 @@ func depositHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.CoreContext) func voteHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.CoreContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + strProposalID := vars[RestProposalID] + + if len(strProposalID) == 0 { + w.WriteHeader(http.StatusBadRequest) + err := errors.New("proposalId required but not specified") + w.Write([]byte(err.Error())) + return + } + + proposalID, err := strconv.ParseInt(strProposalID, 10, 64) + if err != nil { + err := errors.Errorf("proposalID [%d] is not positive", proposalID) + w.Write([]byte(err.Error())) + return + } + var req voteReq - err := buildReq(w, r, &req) + err = buildReq(w, r, &req) if err != nil { return } @@ -146,7 +183,7 @@ func voteHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.CoreContext) ht } // create the message - msg := gov.NewMsgVote(voter, req.ProposalID, voteOptionByte) + msg := gov.NewMsgVote(voter, proposalID, voteOptionByte) err = msg.ValidateBasic() if err != nil { writeErr(&w, http.StatusBadRequest, err.Error()) @@ -161,7 +198,7 @@ func voteHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.CoreContext) ht func queryProposalHandlerFn(storeName string, cdc *wire.Codec, kb keys.Keybase, ctx context.CoreContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) - strProposalID := vars[ProposalRestID] + strProposalID := vars[RestProposalID] if len(strProposalID) == 0 { w.WriteHeader(http.StatusBadRequest) @@ -180,7 +217,7 @@ func queryProposalHandlerFn(storeName string, cdc *wire.Codec, kb keys.Keybase, ctx := context.NewCoreContextFromViper() res, err := ctx.QueryStore(gov.KeyProposal(proposalID), storeName) - if len(res) == 0 || err != nil { + if err != nil || len(res) == 0 { err := errors.Errorf("proposalID [%d] does not exist", proposalID) w.Write([]byte(err.Error())) return @@ -199,10 +236,76 @@ func queryProposalHandlerFn(storeName string, cdc *wire.Codec, kb keys.Keybase, } } +func queryDepositHandlerFn(storeName string, cdc *wire.Codec, kb keys.Keybase, ctx context.CoreContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + strProposalID := vars[RestProposalID] + bechDepositerAddr := vars[RestDepositer] + + if len(strProposalID) == 0 { + w.WriteHeader(http.StatusBadRequest) + err := errors.New("proposalId required but not specified") + w.Write([]byte(err.Error())) + return + } + + proposalID, err := strconv.ParseInt(strProposalID, 10, 64) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + err := errors.Errorf("proposalID [%d] is not positive", proposalID) + w.Write([]byte(err.Error())) + return + } + + if len(bechDepositerAddr) == 0 { + w.WriteHeader(http.StatusBadRequest) + err := errors.New("depositer address required but not specified") + w.Write([]byte(err.Error())) + return + } + + depositerAddr, err := sdk.GetAccAddressBech32(bechDepositerAddr) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + err := errors.Errorf("'%s' needs to be bech32 encoded", RestDepositer) + w.Write([]byte(err.Error())) + return + } + + ctx := context.NewCoreContextFromViper() + + res, err := ctx.QueryStore(gov.KeyDeposit(proposalID, depositerAddr), storeName) + if err != nil || len(res) == 0 { + res, err := ctx.QueryStore(gov.KeyProposal(proposalID), storeName) + if err != nil || len(res) == 0 { + w.WriteHeader(http.StatusNotFound) + err := errors.Errorf("proposalID [%d] does not exist", proposalID) + w.Write([]byte(err.Error())) + return + } + w.WriteHeader(http.StatusNotFound) + err = errors.Errorf("depositer [%s] did not deposit on proposalID [%d]", bechDepositerAddr, proposalID) + w.Write([]byte(err.Error())) + return + } + + var deposit gov.Deposit + cdc.MustUnmarshalBinary(res, &deposit) + depositRest := gov.DepositToRest(deposit) + output, err := wire.MarshalJSONIndent(cdc, depositRest) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } + w.Write(output) + } +} + func queryVoteHandlerFn(storeName string, cdc *wire.Codec, kb keys.Keybase, ctx context.CoreContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) - strProposalID := vars[ProposalRestID] + strProposalID := vars[RestProposalID] bechVoterAddr := vars[RestVoter] if len(strProposalID) == 0 { @@ -214,11 +317,19 @@ func queryVoteHandlerFn(storeName string, cdc *wire.Codec, kb keys.Keybase, ctx proposalID, err := strconv.ParseInt(strProposalID, 10, 64) if err != nil { + w.WriteHeader(http.StatusBadRequest) err := errors.Errorf("proposalID [%s] is not positive", proposalID) w.Write([]byte(err.Error())) return } + if len(bechVoterAddr) == 0 { + w.WriteHeader(http.StatusBadRequest) + err := errors.New("voter address required but not specified") + w.Write([]byte(err.Error())) + return + } + voterAddr, err := sdk.GetAccAddressBech32(bechVoterAddr) if err != nil { w.WriteHeader(http.StatusBadRequest) @@ -229,16 +340,17 @@ func queryVoteHandlerFn(storeName string, cdc *wire.Codec, kb keys.Keybase, ctx ctx := context.NewCoreContextFromViper() - key := []byte(gov.KeyVote(proposalID, voterAddr)) - res, err := ctx.QueryStore(key, storeName) - if len(res) == 0 || err != nil { + res, err := ctx.QueryStore(gov.KeyVote(proposalID, voterAddr), storeName) + if err != nil || len(res) == 0 { res, err := ctx.QueryStore(gov.KeyProposal(proposalID), storeName) - if len(res) == 0 || err != nil { + if err != nil || len(res) == 0 { + w.WriteHeader(http.StatusNotFound) err := errors.Errorf("proposalID [%d] does not exist", proposalID) w.Write([]byte(err.Error())) return } + w.WriteHeader(http.StatusNotFound) err = errors.Errorf("voter [%s] did not vote on proposalID [%d]", bechVoterAddr, proposalID) w.Write([]byte(err.Error())) return @@ -256,3 +368,80 @@ func queryVoteHandlerFn(storeName string, cdc *wire.Codec, kb keys.Keybase, ctx w.Write(output) } } + +func queryProposalsWithParameterFn(storeName string, cdc *wire.Codec, kb keys.Keybase, ctx context.CoreContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + bechVoterAddr := r.URL.Query().Get(RestVoter) + bechDepositerAddr := r.URL.Query().Get(RestDepositer) + + var err error + var voterAddr sdk.Address + var depositerAddr sdk.Address + + if len(bechVoterAddr) != 0 { + voterAddr, err = sdk.GetAccAddressBech32(bechVoterAddr) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + err := errors.Errorf("'%s' needs to be bech32 encoded", RestVoter) + w.Write([]byte(err.Error())) + return + } + } + + if len(bechDepositerAddr) != 0 { + depositerAddr, err = sdk.GetAccAddressBech32(bechDepositerAddr) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + err := errors.Errorf("'%s' needs to be bech32 encoded", RestDepositer) + w.Write([]byte(err.Error())) + return + } + } + + ctx := context.NewCoreContextFromViper() + + res, err := ctx.QueryStore(gov.KeyNextProposalID, storeName) + if err != nil { + err = errors.New("no proposals exist yet and proposalID has not been set") + w.Write([]byte(err.Error())) + return + } + var maxProposalID int64 + cdc.MustUnmarshalBinary(res, &maxProposalID) + + matchingProposals := []gov.ProposalRest{} + + for proposalID := int64(0); proposalID < maxProposalID; proposalID++ { + if voterAddr != nil { + res, err = ctx.QueryStore(gov.KeyVote(proposalID, voterAddr), storeName) + if err != nil || len(res) == 0 { + continue + } + } + + if depositerAddr != nil { + res, err = ctx.QueryStore(gov.KeyDeposit(proposalID, depositerAddr), storeName) + if err != nil || len(res) == 0 { + continue + } + } + + res, err = ctx.QueryStore(gov.KeyProposal(proposalID), storeName) + if err != nil || len(res) == 0 { + continue + } + var proposal gov.Proposal + cdc.MustUnmarshalBinary(res, &proposal) + + matchingProposals = append(matchingProposals, gov.ProposalToRest(proposal)) + } + + output, err := wire.MarshalJSONIndent(cdc, matchingProposals) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } + w.Write(output) + } +} diff --git a/x/gov/depositsvotes.go b/x/gov/depositsvotes.go index bf36e3b0c..5fbdc1600 100644 --- a/x/gov/depositsvotes.go +++ b/x/gov/depositsvotes.go @@ -25,8 +25,9 @@ type Vote struct { // Deposit type Deposit struct { - Depositer sdk.Address `json:"depositer"` // Address of the depositer - Amount sdk.Coins `json:"amount"` // Deposit amount + Depositer sdk.Address `json:"depositer"` // Address of the depositer + ProposalID int64 `json:"proposal_id"` // proposalID of the proposal + Amount sdk.Coins `json:"amount"` // Deposit amount } // ProposalTypeToString for pretty prints of ProposalType @@ -72,6 +73,25 @@ func StringToVoteOption(str string) (VoteOption, sdk.Error) { } //----------------------------------------------------------- +// REST + +// Rest Deposits +type DepositRest struct { + Depositer string `json:"voter"` // address of the voter + ProposalID int64 `json:"proposal_id"` // proposalID of the proposal + Amount sdk.Coins `json:"option"` +} + +// Turn any Deposit to a DepositRest +func DepositToRest(deposit Deposit) DepositRest { + bechAddr := sdk.MustBech32ifyAcc(deposit.Depositer) + return DepositRest{ + Depositer: bechAddr, + ProposalID: deposit.ProposalID, + Amount: deposit.Amount, + } +} + // Rest Votes type VoteRest struct { Voter string `json:"voter"` // address of the voter @@ -79,7 +99,7 @@ type VoteRest struct { Option string `json:"option"` } -// Turn any Vote to a ProposalRest +// Turn any Vote to a VoteRest func VoteToRest(vote Vote) VoteRest { bechAddr, _ := sdk.Bech32ifyAcc(vote.Voter) return VoteRest{ diff --git a/x/gov/keeper.go b/x/gov/keeper.go index bc9153304..7d0eb0406 100644 --- a/x/gov/keeper.go +++ b/x/gov/keeper.go @@ -101,7 +101,7 @@ func (keeper Keeper) setInitialProposalID(ctx sdk.Context, proposalID int64) sdk if bz != nil { return ErrInvalidGenesis(keeper.codespace, "Initial ProposalID already set") } - bz = keeper.cdc.MustMarshalBinary(proposalID) // TODO: switch to MarshalBinaryBare when new go-amino gets added + bz = keeper.cdc.MustMarshalBinary(proposalID) store.Set(KeyNextProposalID, bz) return nil } @@ -112,8 +112,8 @@ func (keeper Keeper) getNewProposalID(ctx sdk.Context) (proposalID int64, err sd if bz == nil { return -1, ErrInvalidGenesis(keeper.codespace, "InitialProposalID never set") } - keeper.cdc.MustUnmarshalBinary(bz, &proposalID) // TODO: switch to UnmarshalBinaryBare when new go-amino gets added - bz = keeper.cdc.MustMarshalBinary(proposalID + 1) // TODO: switch to MarshalBinaryBare when new go-amino gets added + keeper.cdc.MustUnmarshalBinary(bz, &proposalID) + bz = keeper.cdc.MustMarshalBinary(proposalID + 1) store.Set(KeyNextProposalID, bz) return proposalID, nil } @@ -264,7 +264,7 @@ func (keeper Keeper) AddDeposit(ctx sdk.Context, proposalID int64, depositerAddr // Add or update deposit object currDeposit, found := keeper.GetDeposit(ctx, proposalID, depositerAddr) if !found { - newDeposit := Deposit{depositerAddr, depositAmount} + newDeposit := Deposit{depositerAddr, proposalID, depositAmount} keeper.setDeposit(ctx, proposalID, depositerAddr, newDeposit) } else { currDeposit.Amount = currDeposit.Amount.Plus(depositAmount) From 6d9f07dfee4651242785e049ec6d031f1d73bf14 Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Wed, 27 Jun 2018 17:32:06 -0700 Subject: [PATCH 2/3] tools: Add go vet as a linter (#1421) * tools: add go vet * tools: Add go vet as a linter --- .circleci/config.yml | 3 +-- CHANGELOG.md | 9 ++++++--- Makefile | 3 ++- client/tx/broadcast.go | 2 +- client/tx/sign.go | 6 +++--- examples/basecoin/app/app_test.go | 8 ++++---- examples/democoin/x/oracle/oracle_test.go | 6 +++--- x/stake/types/delegation.go | 4 ++-- 8 files changed, 22 insertions(+), 19 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index bb1ad1671..149b5546d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -67,8 +67,7 @@ jobs: name: Lint source command: | export PATH="$GOBIN:$PATH" - gometalinter.v2 --disable-all --enable='golint' --enable='misspell' --vendor ./... - find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs gofmt -d -s + make test_lint test_unit: <<: *defaults parallelism: 1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 70d3c303a..cd11eff37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ BREAKING CHANGES * [stake] remove Tick and add EndBlocker * [stake] introduce concept of unbonding for delegations and validators * `gaiacli stake unbond` replaced with `gaiacli stake begin-unbonding` - * introduced: + * introduced: * `gaiacli stake complete-unbonding` * `gaiacli stake begin-redelegation` * `gaiacli stake complete-redelegation` @@ -34,14 +34,17 @@ FEATURES * Run with `cd x/bank && go test --bench=.` * [tools] make get_tools installs tendermint's linter, and gometalinter * [tools] Switch gometalinter to the stable version -* [tools] Add checking for misspellings and for incorrectly formatted files in circle CI +* [tools] Add the following linters + * misspell + * gofmt + * go vet -composites=false * [server] Default config now creates a profiler at port 6060, and increase p2p send/recv rates * [tests] Add WaitForNextNBlocksTM helper method * [types] Switches internal representation of Int/Uint/Rat to use pointers * [gaiad] unsafe_reset_all now resets addrbook.json * [democoin] add x/oracle, x/assoc -FIXES +FIXES * [gaia] Added self delegation for validators in the genesis creation * [lcd] tests now don't depend on raw json text * [stake] error strings lower case diff --git a/Makefile b/Makefile index 41883cfaf..f2fbdd2b8 100644 --- a/Makefile +++ b/Makefile @@ -108,7 +108,8 @@ test_cover: @bash tests/test_cover.sh test_lint: - gometalinter.v2 --disable-all --enable='golint' --enable='misspell' --vendor ./... + gometalinter.v2 --disable-all --enable='golint' --enable='misspell' --linter='vet:go vet -composites=false:PATH:LINE:MESSAGE' --enable='vet' --vendor ./... + find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs gofmt -d -s benchmark: @go test -bench=. $(PACKAGES_NOCLITEST) diff --git a/client/tx/broadcast.go b/client/tx/broadcast.go index 7f55e97d7..21f576db4 100644 --- a/client/tx/broadcast.go +++ b/client/tx/broadcast.go @@ -9,7 +9,7 @@ import ( // Tx Broadcast Body type BroadcastTxBody struct { - TxBytes string `json="tx"` + TxBytes string `json:"tx"` } // BroadcastTx REST Handler diff --git a/client/tx/sign.go b/client/tx/sign.go index 2d885b049..36a765eed 100644 --- a/client/tx/sign.go +++ b/client/tx/sign.go @@ -11,9 +11,9 @@ import ( // REST request body // TODO does this need to be exposed? type SignTxBody struct { - Name string `json="name"` - Password string `json="password"` - TxBytes string `json="tx"` + Name string `json:"name"` + Password string `json:"password"` + TxBytes string `json:"tx"` } // sign transaction REST Handler diff --git a/examples/basecoin/app/app_test.go b/examples/basecoin/app/app_test.go index c3ba39b3d..81ed40f21 100644 --- a/examples/basecoin/app/app_test.go +++ b/examples/basecoin/app/app_test.go @@ -1,10 +1,10 @@ package app import ( - "os" - "fmt" - "testing" "encoding/json" + "fmt" + "os" + "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -83,7 +83,7 @@ func TestGenesis(t *testing.T) { // InitChain with default stake data. Initializes deliverState and checkState context bapp.InitChain(abci.RequestInitChain{AppStateBytes: []byte(fmt.Sprintf("{\"stake\": %s}", string(genState)))}) - + ctx = bapp.BaseApp.NewContext(true, abci.Header{}) res1 = bapp.accountMapper.GetAccount(ctx, baseAcc.Address) assert.Equal(t, acc, res1) diff --git a/examples/democoin/x/oracle/oracle_test.go b/examples/democoin/x/oracle/oracle_test.go index 27c5aa08b..1cd73b1c1 100644 --- a/examples/democoin/x/oracle/oracle_test.go +++ b/examples/democoin/x/oracle/oracle_test.go @@ -106,9 +106,9 @@ func TestOracle(t *testing.T) { addr3 := []byte("addr3") addr4 := []byte("addr4") valset := &mock.ValidatorSet{[]mock.Validator{ - mock.Validator{addr1, sdk.NewRat(7)}, - mock.Validator{addr2, sdk.NewRat(7)}, - mock.Validator{addr3, sdk.NewRat(1)}, + {addr1, sdk.NewRat(7)}, + {addr2, sdk.NewRat(7)}, + {addr3, sdk.NewRat(1)}, }} key := sdk.NewKVStoreKey("testkey") diff --git a/x/stake/types/delegation.go b/x/stake/types/delegation.go index 235e1e608..410cfbe1d 100644 --- a/x/stake/types/delegation.go +++ b/x/stake/types/delegation.go @@ -101,8 +101,8 @@ type Redelegation struct { ValidatorDstAddr sdk.Address `json:"validator_dst_addr"` // validator redelegation destination owner addr CreationHeight int64 `json:"creation_height"` // height which the redelegation took place MinTime int64 `json:"min_time"` // unix time for redelegation completion - SharesSrc sdk.Rat `json:"shares` // amount of source shares redelegating - SharesDst sdk.Rat `json:"shares` // amount of destination shares redelegating + SharesSrc sdk.Rat `json:"shares_src"` // amount of source shares redelegating + SharesDst sdk.Rat `json:"shares_dst"` // amount of destination shares redelegating } // nolint From 473ac4a38e89fee9b992fb220e20c44db05e1e33 Mon Sep 17 00:00:00 2001 From: Rigel Date: Wed, 27 Jun 2018 21:22:57 -0400 Subject: [PATCH 3/3] Merge PR #1423: PR Template labels --- .github/PULL_REQUEST_TEMPLATE.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index caa541510..521a4c335 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -10,3 +10,4 @@ v If a checkbox is n/a - please still include it but + a little note why * [ ] Updated CHANGELOG.md * [ ] Updated Gaia/Examples * [ ] Squashed all commits, uses message "Merge pull request #XYZ: [title]" ([coding standards](https://github.com/tendermint/coding/blob/master/README.md#merging-a-pr)) +* [ ] Added appropriate labels to PR (ex. wip, ready-for-review, docs)