Skip to content

Commit 89f240e

Browse files
author
HaoyangLiu
authored
Merge pull request #5 from HaoyangLiu/feature/lcd-dev-4
IRISHUB-359: add rest apis of gov and keys for lcd
2 parents 97d3e9a + 2888d16 commit 89f240e

File tree

13 files changed

+916
-4
lines changed

13 files changed

+916
-4
lines changed

client/gov/lcd/flags.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package lcd
2+
3+
const (
4+
RestProposalID = "proposalID"
5+
RestDepositer = "depositer"
6+
RestVoter = "voter"
7+
RestProposalStatus = "status"
8+
storeName = "gov"
9+
)

client/gov/lcd/query.go

Lines changed: 343 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,343 @@
1+
package lcd
2+
3+
import (
4+
"fmt"
5+
sdk "github.com/cosmos/cosmos-sdk/types"
6+
"github.com/cosmos/cosmos-sdk/wire"
7+
"github.com/gorilla/mux"
8+
"github.com/irisnet/irishub/client/context"
9+
govClient "github.com/irisnet/irishub/client/gov"
10+
"github.com/irisnet/irishub/modules/gov"
11+
"net/http"
12+
"strconv"
13+
"github.com/irisnet/irishub/client/utils"
14+
)
15+
16+
func queryProposalHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.HandlerFunc {
17+
return func(w http.ResponseWriter, r *http.Request) {
18+
vars := mux.Vars(r)
19+
strProposalID := vars[RestProposalID]
20+
21+
if len(strProposalID) == 0 {
22+
utils.WriteErrorResponse(w, http.StatusBadRequest, "proposalId required but not specified")
23+
return
24+
}
25+
26+
proposalID, err := strconv.ParseInt(strProposalID, 10, 64)
27+
if err != nil {
28+
utils.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("proposalID [%d] is not positive", proposalID))
29+
return
30+
}
31+
32+
res, err := cliCtx.QueryStore(gov.KeyProposal(proposalID), storeName)
33+
if err != nil || len(res) == 0 {
34+
utils.WriteErrorResponse(w, http.StatusNotFound, fmt.Sprintf("proposalID [%d] does not exist", proposalID))
35+
return
36+
}
37+
38+
var proposal gov.Proposal
39+
cdc.MustUnmarshalBinary(res, &proposal)
40+
proposalResponse, err := govClient.ConvertProposalCoins(cliCtx, proposal)
41+
if err != nil {
42+
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
43+
return
44+
}
45+
output, err := wire.MarshalJSONIndent(cdc, proposalResponse)
46+
if err != nil {
47+
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
48+
return
49+
}
50+
51+
w.Write(output)
52+
}
53+
}
54+
55+
func queryDepositHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.HandlerFunc {
56+
return func(w http.ResponseWriter, r *http.Request) {
57+
vars := mux.Vars(r)
58+
strProposalID := vars[RestProposalID]
59+
bechDepositerAddr := vars[RestDepositer]
60+
61+
if len(strProposalID) == 0 {
62+
utils.WriteErrorResponse(w, http.StatusBadRequest, "proposalId required but not specified")
63+
return
64+
}
65+
66+
proposalID, err := strconv.ParseInt(strProposalID, 10, 64)
67+
if err != nil {
68+
utils.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("proposalID [%d] is not positive", proposalID))
69+
return
70+
}
71+
72+
if len(bechDepositerAddr) == 0 {
73+
utils.WriteErrorResponse(w, http.StatusBadRequest, "depositer address required but not specified")
74+
return
75+
}
76+
77+
depositerAddr, err := sdk.AccAddressFromBech32(bechDepositerAddr)
78+
if err != nil {
79+
utils.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("'%s' needs to be bech32 encoded", RestDepositer))
80+
return
81+
}
82+
83+
res, err := cliCtx.QueryStore(gov.KeyDeposit(proposalID, depositerAddr), storeName)
84+
if err != nil || len(res) == 0 {
85+
res, err := cliCtx.QueryStore(gov.KeyProposal(proposalID), storeName)
86+
if err != nil || len(res) == 0 {
87+
utils.WriteErrorResponse(w, http.StatusNotFound, fmt.Sprintf("proposalID [%d] does not exist", proposalID))
88+
return
89+
}
90+
91+
utils.WriteErrorResponse(w, http.StatusNotFound, fmt.Sprintf("depositer [%s] did not deposit on proposalID [%d]",
92+
bechDepositerAddr, proposalID))
93+
return
94+
}
95+
96+
var deposit gov.Deposit
97+
cdc.MustUnmarshalBinary(res, &deposit)
98+
99+
depositeResponse, err := govClient.ConvertDepositeCoins(cliCtx, deposit)
100+
if err != nil {
101+
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
102+
return
103+
}
104+
output, err := wire.MarshalJSONIndent(cdc, depositeResponse)
105+
if err != nil {
106+
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
107+
return
108+
}
109+
110+
w.Write(output)
111+
}
112+
}
113+
114+
func queryVoteHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.HandlerFunc {
115+
return func(w http.ResponseWriter, r *http.Request) {
116+
vars := mux.Vars(r)
117+
strProposalID := vars[RestProposalID]
118+
bechVoterAddr := vars[RestVoter]
119+
120+
if len(strProposalID) == 0 {
121+
utils.WriteErrorResponse(w, http.StatusBadRequest, "proposalId required but not specified")
122+
return
123+
}
124+
125+
proposalID, err := strconv.ParseInt(strProposalID, 10, 64)
126+
if err != nil {
127+
utils.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("proposalID [%s] is not positive", proposalID))
128+
return
129+
}
130+
131+
if len(bechVoterAddr) == 0 {
132+
utils.WriteErrorResponse(w, http.StatusBadRequest, "voter address required but not specified")
133+
return
134+
}
135+
136+
voterAddr, err := sdk.AccAddressFromBech32(bechVoterAddr)
137+
if err != nil {
138+
utils.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("'%s' needs to be bech32 encoded", RestVoter))
139+
return
140+
}
141+
142+
res, err := cliCtx.QueryStore(gov.KeyVote(proposalID, voterAddr), storeName)
143+
if err != nil || len(res) == 0 {
144+
res, err := cliCtx.QueryStore(gov.KeyProposal(proposalID), storeName)
145+
if err != nil || len(res) == 0 {
146+
utils.WriteErrorResponse(w, http.StatusNotFound, fmt.Sprintf("proposalID [%d] does not exist", proposalID))
147+
return
148+
}
149+
utils.WriteErrorResponse(w, http.StatusNotFound, fmt.Sprintf("voter [%s] did not vote on proposalID [%d]",
150+
bechVoterAddr, proposalID))
151+
return
152+
}
153+
154+
var vote gov.Vote
155+
cdc.MustUnmarshalBinary(res, &vote)
156+
157+
output, err := wire.MarshalJSONIndent(cdc, vote)
158+
if err != nil {
159+
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
160+
return
161+
}
162+
163+
w.Write(output)
164+
}
165+
}
166+
167+
// nolint: gocyclo
168+
// todo: Split this functionality into helper functions to remove the above
169+
func queryVotesOnProposalHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.HandlerFunc {
170+
return func(w http.ResponseWriter, r *http.Request) {
171+
vars := mux.Vars(r)
172+
strProposalID := vars[RestProposalID]
173+
174+
if len(strProposalID) == 0 {
175+
utils.WriteErrorResponse(w, http.StatusBadRequest, "proposalId required but not specified")
176+
return
177+
}
178+
179+
proposalID, err := strconv.ParseInt(strProposalID, 10, 64)
180+
if err != nil {
181+
utils.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("proposalID [%s] is not positive", proposalID))
182+
return
183+
}
184+
185+
res, err := cliCtx.QueryStore(gov.KeyProposal(proposalID), storeName)
186+
if err != nil || len(res) == 0 {
187+
utils.WriteErrorResponse(w, http.StatusNotFound, fmt.Sprintf("proposalID [%d] does not exist", proposalID))
188+
return
189+
}
190+
191+
var proposal gov.Proposal
192+
cdc.MustUnmarshalBinary(res, &proposal)
193+
194+
if proposal.GetStatus() != gov.StatusVotingPeriod {
195+
utils.WriteErrorResponse(w, http.StatusNotFound, fmt.Sprintf("proposal is not in Voting Period", proposalID))
196+
return
197+
}
198+
199+
res2, err := cliCtx.QuerySubspace(gov.KeyVotesSubspace(proposalID), storeName)
200+
if err != nil {
201+
utils.WriteErrorResponse(w, http.StatusNotFound, "ProposalID doesn't exist")
202+
return
203+
}
204+
205+
var votes []gov.Vote
206+
207+
for i := 0; i < len(res2); i++ {
208+
var vote gov.Vote
209+
cdc.MustUnmarshalBinary(res2[i].Value, &vote)
210+
votes = append(votes, vote)
211+
}
212+
213+
output, err := wire.MarshalJSONIndent(cdc, votes)
214+
if err != nil {
215+
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
216+
return
217+
}
218+
219+
w.Write(output)
220+
}
221+
}
222+
223+
// nolint: gocyclo
224+
// todo: Split this functionality into helper functions to remove the above
225+
func queryProposalsWithParameterFn(cdc *wire.Codec, cliCtx context.CLIContext) http.HandlerFunc {
226+
return func(w http.ResponseWriter, r *http.Request) {
227+
bechVoterAddr := r.URL.Query().Get(RestVoter)
228+
bechDepositerAddr := r.URL.Query().Get(RestDepositer)
229+
strProposalStatus := r.URL.Query().Get(RestProposalStatus)
230+
231+
var err error
232+
var voterAddr sdk.AccAddress
233+
var depositerAddr sdk.AccAddress
234+
var proposalStatus gov.ProposalStatus
235+
236+
if len(bechVoterAddr) != 0 {
237+
voterAddr, err = sdk.AccAddressFromBech32(bechVoterAddr)
238+
if err != nil {
239+
utils.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("'%s' needs to be bech32 encoded", RestVoter))
240+
return
241+
}
242+
}
243+
244+
if len(bechDepositerAddr) != 0 {
245+
depositerAddr, err = sdk.AccAddressFromBech32(bechDepositerAddr)
246+
if err != nil {
247+
utils.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("'%s' needs to be bech32 encoded", RestDepositer))
248+
return
249+
}
250+
}
251+
252+
if len(strProposalStatus) != 0 {
253+
proposalStatus, err = gov.ProposalStatusFromString(strProposalStatus)
254+
if err != nil {
255+
utils.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("'%s' is not a valid Proposal Status", strProposalStatus))
256+
return
257+
}
258+
}
259+
260+
res, err := cliCtx.QueryStore(gov.KeyNextProposalID, storeName)
261+
if err != nil {
262+
utils.WriteErrorResponse(w, http.StatusNotFound, "no proposals exist yet and proposalID has not been set")
263+
return
264+
}
265+
266+
var maxProposalID int64
267+
cdc.MustUnmarshalBinary(res, &maxProposalID)
268+
269+
matchingProposals := []govClient.TextProposalResponse{}
270+
271+
for proposalID := int64(0); proposalID < maxProposalID; proposalID++ {
272+
if voterAddr != nil {
273+
res, err = cliCtx.QueryStore(gov.KeyVote(proposalID, voterAddr), storeName)
274+
if err != nil || len(res) == 0 {
275+
continue
276+
}
277+
}
278+
279+
if depositerAddr != nil {
280+
res, err = cliCtx.QueryStore(gov.KeyDeposit(proposalID, depositerAddr), storeName)
281+
if err != nil || len(res) == 0 {
282+
continue
283+
}
284+
}
285+
286+
res, err = cliCtx.QueryStore(gov.KeyProposal(proposalID), storeName)
287+
if err != nil || len(res) == 0 {
288+
continue
289+
}
290+
291+
var proposal gov.Proposal
292+
cdc.MustUnmarshalBinary(res, &proposal)
293+
294+
if len(strProposalStatus) != 0 {
295+
if proposal.GetStatus() != proposalStatus {
296+
continue
297+
}
298+
}
299+
proposalResponse, err := govClient.ConvertProposalCoins(cliCtx, proposal)
300+
if err != nil {
301+
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
302+
return
303+
}
304+
matchingProposals = append(matchingProposals, proposalResponse)
305+
}
306+
307+
output, err := wire.MarshalJSONIndent(cdc, matchingProposals)
308+
if err != nil {
309+
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
310+
return
311+
}
312+
313+
w.Write(output)
314+
}
315+
}
316+
317+
// nolint: gocyclo
318+
func queryConfigHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.HandlerFunc {
319+
return func(w http.ResponseWriter, r *http.Request) {
320+
res, err := cliCtx.QuerySubspace([]byte(gov.Prefix), storeName)
321+
if err != nil {
322+
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
323+
return
324+
}
325+
var kvs []govClient.KvPair
326+
for _, kv := range res {
327+
var v string
328+
cdc.UnmarshalBinary(kv.Value, &v)
329+
kv := govClient.KvPair{
330+
K: string(kv.Key),
331+
V: v,
332+
}
333+
kvs = append(kvs, kv)
334+
}
335+
output, err := wire.MarshalJSONIndent(cdc, kvs)
336+
if err != nil {
337+
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
338+
return
339+
}
340+
341+
w.Write(output)
342+
}
343+
}

client/gov/lcd/rest.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package lcd
2+
3+
import (
4+
"github.com/cosmos/cosmos-sdk/wire"
5+
"fmt"
6+
"github.com/irisnet/irishub/client/context"
7+
"github.com/gorilla/mux"
8+
)
9+
10+
// RegisterRoutes - Central function to define routes that get registered by the main application
11+
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *wire.Codec) {
12+
r.HandleFunc("/gov/proposals", postProposalHandlerFn(cdc, cliCtx)).Methods("POST")
13+
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits", RestProposalID), depositHandlerFn(cdc, cliCtx)).Methods("POST")
14+
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/votes", RestProposalID), voteHandlerFn(cdc, cliCtx)).Methods("POST")
15+
16+
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}", RestProposalID), queryProposalHandlerFn(cdc, cliCtx)).Methods("GET")
17+
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits/{%s}", RestProposalID, RestDepositer), queryDepositHandlerFn(cdc, cliCtx)).Methods("GET")
18+
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/votes/{%s}", RestProposalID, RestVoter), queryVoteHandlerFn(cdc, cliCtx)).Methods("GET")
19+
20+
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/votes", RestProposalID), queryVotesOnProposalHandlerFn(cdc, cliCtx)).Methods("GET")
21+
22+
r.HandleFunc("/gov/proposals", queryProposalsWithParameterFn(cdc, cliCtx)).Methods("GET")
23+
24+
r.HandleFunc("/gov/params", queryConfigHandlerFn(cdc, cliCtx)).Methods("GET")
25+
}

0 commit comments

Comments
 (0)