go-dvote/service/censusmanager.go
service/censusmanager.goRaw
1package censusmanager
2
3import (
4 "encoding/json"
5 "fmt"
6 "log"
7 "net/http"
8 "strconv"
9 "strings"
10 "time"
11
12 "github.com/vocdoni/dvote-census/tree"
13 "github.com/vocdoni/dvote-relay/crypto/signature"
14)
15
16const authTimeWindow = 10 // Time window (seconds) in which TimeStamp will be accepted if auth enabled
17
18var authPubKey string
19
20var T tree.Tree // MerkleTree dvote-census library
21var S signature.SignKeys // Signature dvote-relay library
22
23type Claim struct {
24 CensusID string `json:"censusID"` // References to MerkleTree namespace
25 ClaimData string `json:"claimData"` // Data to add to the MerkleTree
26 ProofData string `json:"proofData"` // MerkleProof to check
27 TimeStamp string `json:"timeStamp"` // Unix TimeStamp in seconds
28 Signature string `json:"signature"` // Signature as Hexadecimal String
29}
30
31type Result struct {
32 Error bool `json:"error"`
33 Response string `json:"response"`
34}
35
36func reply(resp *Result, w http.ResponseWriter) {
37 err := json.NewEncoder(w).Encode(resp)
38 if err != nil {
39 http.Error(w, err.Error(), 500)
40 } else {
41 w.Header().Set("content-type", "application/json")
42 }
43}
44
45func checkRequest(w http.ResponseWriter, req *http.Request) bool {
46 if req.Body == nil {
47 http.Error(w, "Please send a request body", 400)
48 return false
49 }
50 return true
51}
52
53func checkAuth(timestamp, signature, message string) bool {
54 if len(authPubKey) < 1 {
55 return true
56 }
57 currentTime := int64(time.Now().Unix())
58 timeStampRemote, err := strconv.ParseInt(timestamp, 10, 32)
59 if err != nil {
60 log.Printf("Cannot parse timestamp data %s\n", err)
61 return false
62 }
63 if timeStampRemote < currentTime+authTimeWindow &&
64 timeStampRemote > currentTime-authTimeWindow {
65 v, err := S.Verify(message, signature, authPubKey)
66 if err != nil {
67 log.Printf("Verification error: %s\n", err)
68 }
69 return v
70 }
71 return false
72}
73
74func claimHandler(w http.ResponseWriter, req *http.Request, op string) {
75 var c Claim
76 var resp Result
77 if ok := checkRequest(w, req); !ok {
78 return
79 }
80 // Decode JSON
81 err := json.NewDecoder(req.Body).Decode(&c)
82 if err != nil {
83 http.Error(w, err.Error(), 400)
84 return
85 }
86
87 // Process data
88 log.Printf("Received censusID:{%s} claimData:{%s} proofData:{%s} timeStamp:{%s} signature:{%s}\n",
89 c.CensusID, c.ClaimData, c.ProofData, c.TimeStamp, c.Signature)
90 resp.Error = false
91 resp.Response = ""
92
93 if len(c.CensusID) > 0 {
94 T.Namespace = c.CensusID
95 } else {
96 resp.Error = true
97 resp.Response = "censusID is not valid"
98 reply(&resp, w)
99 return
100 }
101
102 if len(c.ClaimData) < 0 {
103 resp.Error = true
104 resp.Response = "data not valid"
105 reply(&resp, w)
106 return
107 }
108
109 if op == "add" {
110 msg := fmt.Sprintf("%s%s%s", c.CensusID, c.ClaimData, c.TimeStamp)
111 if auth := checkAuth(c.TimeStamp, c.Signature, msg); auth {
112 if strings.HasPrefix(c.CensusID, "0x") {
113 resp.Error = true
114 resp.Response = "add claim to snapshot is not allowed"
115 } else {
116 err = T.AddClaim([]byte(c.ClaimData))
117 }
118 } else {
119 resp.Error = true
120 resp.Response = "invalid authentication"
121 }
122 }
123
124 if op == "gen" {
125 resp.Response, err = T.GenProof([]byte(c.ClaimData))
126 }
127
128 if op == "root" {
129 resp.Response = T.GetRoot()
130 }
131
132 if op == "dump" {
133 values, err := T.Dump()
134 if err != nil {
135 resp.Error = true
136 resp.Response = fmt.Sprint(err)
137 } else {
138 jValues, err := json.Marshal(values)
139 if err != nil {
140 resp.Error = true
141 resp.Response = fmt.Sprint(err)
142 } else {
143 resp.Response = string(jValues)
144 }
145 }
146 }
147
148 if op == "snapshot" {
149 msg := fmt.Sprintf("%s%s%s", c.CensusID, c.ClaimData, c.TimeStamp)
150 if auth := checkAuth(c.TimeStamp, c.Signature, msg); auth {
151 if strings.HasPrefix(c.CensusID, "0x") {
152 resp.Error = true
153 resp.Response = "snapshot an snapshot makes no sense"
154 } else {
155 snapshotNamespace, err := T.Snapshot()
156 if err != nil {
157 resp.Error = true
158 resp.Response = fmt.Sprint(err)
159 } else {
160 resp.Response = snapshotNamespace
161 }
162 }
163 } else {
164 resp.Error = true
165 resp.Response = "invalid authentication"
166 }
167 }
168
169 if op == "check" {
170 if len(c.ProofData) < 1 {
171 resp.Error = true
172 resp.Response = "proofData not provided"
173 } else {
174 validProof, _ := T.CheckProof([]byte(c.ClaimData), c.ProofData)
175 if validProof {
176 resp.Response = "valid"
177 } else {
178 resp.Response = "invalid"
179 }
180 }
181 }
182
183 reply(&resp, w)
184}
185
186func Listen(port int, proto string, pubKey string) {
187 srv := &http.Server{
188 Addr: fmt.Sprintf(":%d", port),
189 ReadHeaderTimeout: 4 * time.Second,
190 ReadTimeout: 4 * time.Second,
191 WriteTimeout: 4 * time.Second,
192 IdleTimeout: 3 * time.Second,
193 }
194
195 http.HandleFunc("/addClaim", func(w http.ResponseWriter, r *http.Request) {
196 claimHandler(w, r, "add")
197 })
198 http.HandleFunc("/genProof", func(w http.ResponseWriter, r *http.Request) {
199 claimHandler(w, r, "gen")
200 })
201 http.HandleFunc("/checkProof", func(w http.ResponseWriter, r *http.Request) {
202 claimHandler(w, r, "check")
203 })
204 http.HandleFunc("/getRoot", func(w http.ResponseWriter, r *http.Request) {
205 claimHandler(w, r, "root")
206 })
207 http.HandleFunc("/snapshot", func(w http.ResponseWriter, r *http.Request) {
208 claimHandler(w, r, "snapshot")
209 })
210 http.HandleFunc("/dump", func(w http.ResponseWriter, r *http.Request) {
211 claimHandler(w, r, "dump")
212 })
213
214 if len(pubKey) > 1 {
215 log.Printf("Enabling signature authentication with %s\n", pubKey)
216 authPubKey = pubKey
217 } else {
218 authPubKey = ""
219 }
220
221 if proto == "https" {
222 log.Print("Starting server in https mode")
223 if err := srv.ListenAndServeTLS("server.crt", "server.key"); err != nil {
224 panic(err)
225 }
226 }
227 if proto == "http" {
228 log.Print("Starting server in http mode")
229 srv.SetKeepAlivesEnabled(false)
230 if err := srv.ListenAndServe(); err != nil {
231 panic(err)
232 }
233 }
234}